Prepare and Preprocess Phase

Meta data

Fitbit data dictionary

Import libraries

library(tidyverse)
library(caret)
library(skimr)
library(janitor)
library(lubridate) # working with dates
library(RColorBrewer) # color palette
library(ggcorrplot) # Visualization of a correlation matrix using ggplot2


# display.brewer.all(colorblindFriendly = TRUE)

Load datasets

# Clean environment
rm(list = ls())

daily_activity <-
  read_csv("dailyActivity_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

daily_sleep <- read_csv("sleepDay_merged.csv",
  trim_ws = TRUE,
  show_col_types = FALSE
)

hourly_calories <-
  read_csv("hourlyCalories_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

hourly_intensities <-
  read_csv("hourlyIntensities_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )
hourly_steps <-
  read_csv("hourlySteps_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

minute_sleep <-
  read_csv("minuteSleep_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

weight_logs <-
  read_csv("weightLogInfo_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

seconds_heartrate <-
  read_csv("heartrate_seconds_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

# Remove trailing spaces (trim_ws = TRUE)

Clean data sets

Clean the daily_activity data set

# Check daily_activity data set before cleaning
glimpse(daily_activity)
Rows: 940
Columns: 15
$ Id                       <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
$ ActivityDate             <chr> "4/12/2016", "4/13/2016", "4/14/2016", "4/15/2016", "4/16/2016", "4/17/2016", "4/1…
$ TotalSteps               <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019, 15506, 10544, 9819, 12764, 14371, 1…
$ TotalDistance            <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80…
$ TrackerDistance          <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80…
$ LoggedActivitiesDistance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ VeryActiveDistance       <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.53, 1.96, 1.34, 4.76, 2.81, 2.92, 5.29…
$ ModeratelyActiveDistance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.32, 0.48, 0.35, 1.12, 0.87, 0.21, 0.57…
$ LightActiveDistance      <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.03, 4.24, 4.65, 2.24, 5.36, 3.28, 3.94…
$ SedentaryActiveDistance  <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00…
$ VeryActiveMinutes        <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 41, 39, 73, 31, 78, 48, 16, 52, 33, 41…
$ FairlyActiveMinutes      <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21, 5, 14, 23, 11, 28, 12, 34, 35, 15, …
$ LightlyActiveMinutes     <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, 211, 130, 262, 238, 216, 279, 243, 18…
$ SedentaryMinutes         <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818, 838, 1217, 732, 709, 814, 833, 1108…
$ Calories                 <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775, 1827, 1949, 1788, 2013…
# Check missing values and duplicates
cat(
  "\n",
  "Missing values:",
  sum(is.na(daily_activity)),
  "\n",
  "Duplicate values:",
  sum(duplicated(daily_activity)),
  "\n",
  "Unique Ids:",
  n_distinct(daily_activity$Id)
)

 Missing values: 0 
 Duplicate values: 0 
 Unique Ids: 33

Let us clean: - Change column names to lower case because R is case sensitive - Change “Id” from double to a character because the number represents a category - Change “ActivityDate” from char to date

# Clean daily_activity data set

daily_activity <-
  # Clean column names
  clean_names(daily_activity) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(activity_date = as.Date(activity_date,
                                 format = "%m/%d/%Y")) %>% # from chr to date
  # Remove duplicate rows
  distinct()

# Check daily_activity data set after cleaning
glimpse(daily_activity)
Rows: 940
Columns: 15
$ id                         <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "150396036…
$ activity_date              <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-16, 2016-04-17, 2016-04…
$ total_steps                <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019, 15506, 10544, 9819, 12764, 14371,…
$ total_distance             <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.…
$ tracker_distance           <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.…
$ logged_activities_distance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ very_active_distance       <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.53, 1.96, 1.34, 4.76, 2.81, 2.92, 5.…
$ moderately_active_distance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.32, 0.48, 0.35, 1.12, 0.87, 0.21, 0.…
$ light_active_distance      <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.03, 4.24, 4.65, 2.24, 5.36, 3.28, 3.…
$ sedentary_active_distance  <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.…
$ very_active_minutes        <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 41, 39, 73, 31, 78, 48, 16, 52, 33, …
$ fairly_active_minutes      <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21, 5, 14, 23, 11, 28, 12, 34, 35, 15…
$ lightly_active_minutes     <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, 211, 130, 262, 238, 216, 279, 243, …
$ sedentary_minutes          <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818, 838, 1217, 732, 709, 814, 833, 11…
$ calories                   <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775, 1827, 1949, 1788, 20…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(daily_activity)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_activity)))

 Missing values: 0 
 Duplicate values: 0
# Let us print summary statistic to have a better idea of the data set
daily_activity %>%
  summary()
      id            activity_date         total_steps    total_distance   tracker_distance
 Length:940         Min.   :2016-04-12   Min.   :    0   Min.   : 0.000   Min.   : 0.000  
 Class :character   1st Qu.:2016-04-19   1st Qu.: 3790   1st Qu.: 2.620   1st Qu.: 2.620  
 Mode  :character   Median :2016-04-26   Median : 7406   Median : 5.245   Median : 5.245  
                    Mean   :2016-04-26   Mean   : 7638   Mean   : 5.490   Mean   : 5.475  
                    3rd Qu.:2016-05-04   3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.: 7.710  
                    Max.   :2016-05-12   Max.   :36019   Max.   :28.030   Max.   :28.030  
 logged_activities_distance very_active_distance moderately_active_distance light_active_distance
 Min.   :0.0000             Min.   : 0.000       Min.   :0.0000             Min.   : 0.000       
 1st Qu.:0.0000             1st Qu.: 0.000       1st Qu.:0.0000             1st Qu.: 1.945       
 Median :0.0000             Median : 0.210       Median :0.2400             Median : 3.365       
 Mean   :0.1082             Mean   : 1.503       Mean   :0.5675             Mean   : 3.341       
 3rd Qu.:0.0000             3rd Qu.: 2.053       3rd Qu.:0.8000             3rd Qu.: 4.782       
 Max.   :4.9421             Max.   :21.920       Max.   :6.4800             Max.   :10.710       
 sedentary_active_distance very_active_minutes fairly_active_minutes lightly_active_minutes sedentary_minutes
 Min.   :0.000000          Min.   :  0.00      Min.   :  0.00        Min.   :  0.0          Min.   :   0.0   
 1st Qu.:0.000000          1st Qu.:  0.00      1st Qu.:  0.00        1st Qu.:127.0          1st Qu.: 729.8   
 Median :0.000000          Median :  4.00      Median :  6.00        Median :199.0          Median :1057.5   
 Mean   :0.001606          Mean   : 21.16      Mean   : 13.56        Mean   :192.8          Mean   : 991.2   
 3rd Qu.:0.000000          3rd Qu.: 32.00      3rd Qu.: 19.00        3rd Qu.:264.0          3rd Qu.:1229.5   
 Max.   :0.110000          Max.   :210.00      Max.   :143.00        Max.   :518.0          Max.   :1440.0   
    calories   
 Min.   :   0  
 1st Qu.:1828  
 Median :2134  
 Mean   :2304  
 3rd Qu.:2793  
 Max.   :4900  

This summary helps us explore quickly each attribute. We notice that some attributes have minimum value of zero (total_step, total_distance, calories). Let us explore this observation.

# Check where total_steps is zero
filter(daily_activity, total_steps == 0)

We found 77 observations where total_steps is zero. We should delete these observations so that they do not affect our the mean and median. If total_step is zero that means that the person did not wear the Fitbit.

# Check where calories is zero
filter(daily_activity, calories == 0)
# Check where total_distance is zero
filter(daily_activity, total_distance == 0)

From our inspection above, we can see that we just need to delete the entries where total_steps is zero and will take take care of the rest.

daily_activity_clean <-
  filter(daily_activity,
         total_steps != 0,
         total_distance != 0,
         calories != 0)
daily_activity_clean
NA
names(daily_activity)
 [1] "id"                         "activity_date"              "total_steps"               
 [4] "total_distance"             "tracker_distance"           "logged_activities_distance"
 [7] "very_active_distance"       "moderately_active_distance" "light_active_distance"     
[10] "sedentary_active_distance"  "very_active_minutes"        "fairly_active_minutes"     
[13] "lightly_active_minutes"     "sedentary_minutes"          "calories"                  
# Check the attributes again

cat("Before deleting the entries\n\n")
Before deleting the entries
select(daily_activity,total_steps,total_distance,calories) %>%
  summary()
  total_steps    total_distance      calories   
 Min.   :    0   Min.   : 0.000   Min.   :   0  
 1st Qu.: 3790   1st Qu.: 2.620   1st Qu.:1828  
 Median : 7406   Median : 5.245   Median :2134  
 Mean   : 7638   Mean   : 5.490   Mean   :2304  
 3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.:2793  
 Max.   :36019   Max.   :28.030   Max.   :4900  
cat("\n\n\n",
    "\t\t vs",
    "\n\n\n")



         vs 
cat("After deleting the entries\n\n")
After deleting the entries
select(daily_activity_clean, total_steps, total_distance, calories) %>%
  summary()
  total_steps    total_distance      calories   
 Min.   :    8   Min.   : 0.010   Min.   :  52  
 1st Qu.: 4927   1st Qu.: 3.373   1st Qu.:1857  
 Median : 8054   Median : 5.590   Median :2220  
 Mean   : 8329   Mean   : 5.986   Mean   :2362  
 3rd Qu.:11096   3rd Qu.: 7.905   3rd Qu.:2832  
 Max.   :36019   Max.   :28.030   Max.   :4900  

We can see that the observation we removed affected our mean and median.

Clean the daily_sleep data set

# Check daily_sleep data set before cleaning
glimpse(daily_sleep)
Rows: 413
Columns: 5
$ Id                 <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503…
$ SleepDay           <chr> "4/12/2016 12:00:00 AM", "4/13/2016 12:00:00 AM", "4/15/2016 12:00:00 AM", "4/16/2016 12…
$ TotalSleepRecords  <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ TotalMinutesAsleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361, 430, 277, 245, 366, 341, 404, 369, 277, 273…
$ TotalTimeInBed     <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384, 449, 323, 274, 393, 354, 425, 396, 309, 296…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(daily_sleep)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_sleep)),
  "\n",
  "Unique Ids:",
  n_distinct(daily_sleep$Id)
    )

 Missing values: 0 
 Duplicate values: 3 
 Unique Ids: 24

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “SleepDay” from char to date. Since the time component of this column is the same for each observation”12:00:00 AM”, we can remove it. This will helps us merged this data set with daily_activity later
  • Delete duplicates (3 observations are duplicates)
# Clean daily_sleep data set

daily_sleep_clean <-
  # Clean column names
  clean_names(daily_sleep) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(sleep_day = as.Date(sleep_day,
                             format = "%m/%d/%Y")) %>% # from chr to date
  # Remove duplicate rows
  distinct()

# Check clean daily_sleep data set
glimpse(daily_sleep_clean)
Rows: 410
Columns: 5
$ id                   <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1…
$ sleep_day            <date> 2016-04-12, 2016-04-13, 2016-04-15, 2016-04-16, 2016-04-17, 2016-04-19, 2016-04-20, 2…
$ total_sleep_records  <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ total_minutes_asleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361, 430, 277, 245, 366, 341, 404, 369, 277, 2…
$ total_time_in_bed    <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384, 449, 323, 274, 393, 354, 425, 396, 309, 2…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(daily_sleep_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_sleep_clean)))

 Missing values: 0 
 Duplicate values: 0

Clean the hourly data sets (hourly_calories, hourly_intensities, and hourly_steps)

# Check hourly_calories data set before cleaning
glimpse(hourly_calories)
Rows: 22,099
Columns: 3
$ Id           <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
$ ActivityHour <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM…
$ Calories     <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132, 100,…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_calories)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_calories)))

 Missing values: 0 
 Duplicate values: 0
# Check hourly_intensities data set before cleaning
glimpse(hourly_intensities)
Rows: 22,099
Columns: 4
$ Id               <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150396…
$ ActivityHour     <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:0…
$ TotalIntensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14,…
$ AverageIntensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667, …
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_intensities)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_intensities)))

 Missing values: 0 
 Duplicate values: 0
# Check hourly_steps data set before cleaning
glimpse(hourly_steps)
Rows: 22,099
Columns: 3
$ Id           <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
$ ActivityHour <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM…
$ StepTotal    <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, 558, …
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_steps)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_steps)))

 Missing values: 0 
 Duplicate values: 0

Join hourly data sets to create a hourly_actitvity data set

These data sets shared the same Id and Activity_hour, let us join them into a new data set (hourly_activity) before we clean them.

# Join the hourly data sets (hourly_calories, hourly_intensities, and hourly_steps)

hourly_activity <-
  inner_join(hourly_calories,
             hourly_intensities,
             by = c("Id", "ActivityHour"))

hourly_activity <-
  inner_join(hourly_activity, hourly_steps, by = c("Id", "ActivityHour"))
# Check hourly_activity data set before cleaning
glimpse(hourly_activity)
Rows: 22,099
Columns: 6
$ Id               <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150396…
$ ActivityHour     <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:0…
$ Calories         <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132, …
$ TotalIntensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14,…
$ AverageIntensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667, …
$ StepTotal        <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, 5…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_activity)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_activity)))

 Missing values: 0 
 Duplicate values: 0

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “ActivityHour” from char to datetime

Note:The default timezone is UTC.

# Clean hourly_activity data set

hourly_activity_clean <-
  # Clean column names
  clean_names(hourly_activity) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(activity_hour = as_datetime(activity_hour,
                                     format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(hourly_activity_clean)
Rows: 22,099
Columns: 6
$ id                <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503…
$ activity_hour     <dttm> 2016-04-12 00:00:00, 2016-04-12 01:00:00, 2016-04-12 02:00:00, 2016-04-12 03:00:00, 2016…
$ calories          <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132,…
$ total_intensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14…
$ average_intensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667,…
$ step_total        <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, …
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(hourly_activity_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_activity_clean)))

 Missing values: 0 
 Duplicate values: 0
# as_datetime() converts with default timezone = "UTC"

Clean the minute_sleep data set

# Check minute_sleep data set before cleaning
glimpse(minute_sleep)
Rows: 188,521
Columns: 4
$ Id    <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 15039…
$ date  <chr> "4/12/2016 2:47:30 AM", "4/12/2016 2:48:30 AM", "4/12/2016 2:49:30 AM", "4/12/2016 2:50:30 AM", "4/12…
$ value <dbl> 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ logId <dbl> 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 1138056458…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(minute_sleep)),
    "\n",
    "Duplicate values:",
    sum(duplicated(minute_sleep)),
    "\n",
  "Unique Ids:",
  n_distinct(minute_sleep$Id))

 Missing values: 0 
 Duplicate values: 543 
 Unique Ids: 24

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “date” from char to datetime
  • Change “value” from double to factor. Value indicates the sleep state: 1 = asleep, 2 = restless, 3 = awake. See: Fitbit data dictionary
  • Remove duplicate values: 543
# Clean minute_sleep data set

minute_sleep_clean <-
  # Clean column names
  clean_names(minute_sleep) %>%
  # Correct column types
  mutate(value = as.factor(value)) %>% # from double to chr
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(date = as_datetime(date,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # From chr to datetime
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(minute_sleep_clean)
Rows: 187,978
Columns: 4
$ id     <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1…
$ date   <dttm> 2016-04-12 02:47:30, 2016-04-12 02:48:30, 2016-04-12 02:49:30, 2016-04-12 02:50:30, 2016-04-12 02:5…
$ value  <fct> 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ log_id <dbl> 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 113805645…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(minute_sleep_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(minute_sleep_clean)))

 Missing values: 0 
 Duplicate values: 0

Clean the seconds_heartrate data set

# Check seconds_heartrate set before cleaning
glimpse(seconds_heartrate)
Rows: 2,483,658
Columns: 3
$ Id    <dbl> 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 20224…
$ Time  <chr> "4/12/2016 7:21:00 AM", "4/12/2016 7:21:05 AM", "4/12/2016 7:21:10 AM", "4/12/2016 7:21:20 AM", "4/12…
$ Value <dbl> 97, 102, 105, 103, 101, 95, 91, 93, 94, 93, 92, 89, 83, 61, 60, 61, 61, 57, 54, 55, 58, 60, 59, 57, 5…
# Check missing values and duplicates
cat(
  "\n",
  "Missing values:", sum(is.na(seconds_heartrate)),
  "\n",
  "Duplicate values:", sum(duplicated(seconds_heartrate))
)

 Missing values: 0 
 Duplicate values: 0

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “Time” from char to datetime and rename it date_time
  • Rename “Value” to heart_rate Fitbit data dictionary
# Clean seconds_heartrate data set

seconds_heartrate_clean <-
  # Clean column names
  clean_names(seconds_heartrate) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(time = as_datetime(time,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Rename columns
  rename(date_time = time,
         heart_rate = value) %>%
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(seconds_heartrate_clean)
Rows: 2,483,658
Columns: 3
$ id         <chr> "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "2022484408"…
$ date_time  <dttm> 2016-04-12 07:21:00, 2016-04-12 07:21:05, 2016-04-12 07:21:10, 2016-04-12 07:21:20, 2016-04-12 …
$ heart_rate <dbl> 97, 102, 105, 103, 101, 95, 91, 93, 94, 93, 92, 89, 83, 61, 60, 61, 61, 57, 54, 55, 58, 60, 59, …
# Check missing values and duplicates after cleaning

cat("\n",
    "Missing values:",
    sum(is.na(seconds_heartrate_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(seconds_heartrate_clean)))

 Missing values: 0 
 Duplicate values: 0
# as_datetime() converts with default timezone = "UTC"

Clean the weight_logs data set

# Check weight_logs set before cleaning

glimpse(weight_logs)
Rows: 67
Columns: 8
$ Id             <dbl> 1503960366, 1503960366, 1927972279, 2873212765, 2873212765, 4319703577, 4319703577, 45586099…
$ Date           <chr> "5/2/2016 11:59:59 PM", "5/3/2016 11:59:59 PM", "4/13/2016 1:08:52 AM", "4/21/2016 11:59:59 …
$ WeightKg       <dbl> 52.6, 52.6, 133.5, 56.7, 57.3, 72.4, 72.3, 69.7, 70.3, 69.9, 69.2, 69.1, 90.7, 62.5, 62.1, 6…
$ WeightPounds   <dbl> 115.9631, 115.9631, 294.3171, 125.0021, 126.3249, 159.6147, 159.3942, 153.6622, 154.9850, 15…
$ Fat            <dbl> 22, NA, NA, NA, NA, 25, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ BMI            <dbl> 22.65, 22.65, 47.54, 21.45, 21.69, 27.45, 27.38, 27.25, 27.46, 27.32, 27.04, 27.00, 28.00, 2…
$ IsManualReport <lgl> TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, …
$ LogId          <dbl> 1.462234e+12, 1.462320e+12, 1.460510e+12, 1.461283e+12, 1.463098e+12, 1.460938e+12, 1.462406…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(weight_logs)),
    "\n",
    "Duplicate values:",
    sum(duplicated(weight_logs)))

 Missing values: 65 
 Duplicate values: 0

Let us clean: - Change column names to lower case because R is case sensitive - Change “Id” from double to a character because the number represents a category - Change “Date” from char to datetime and rename it date_time - Change NA to 0 in the column “fat”

# Clean  weight_logs data set

weight_logs_clean <-
  # Clean column names
  clean_names(weight_logs) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(date = as_datetime(date,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Rename columns
  rename(date_time = date) %>%
  # Remove duplicate rows
  distinct()



# Change NA to 0 in the column "fat"
weight_logs_clean$fat[is.na(weight_logs$fat)] <- 0
Warning: Unknown or uninitialised column: `fat`.
# Check clean daily_activity data set
glimpse(weight_logs_clean)
Rows: 67
Columns: 8
$ id               <chr> "1503960366", "1503960366", "1927972279", "2873212765", "2873212765", "4319703577", "43197…
$ date_time        <dttm> 2016-05-02 23:59:59, 2016-05-03 23:59:59, 2016-04-13 01:08:52, 2016-04-21 23:59:59, 2016-…
$ weight_kg        <dbl> 52.6, 52.6, 133.5, 56.7, 57.3, 72.4, 72.3, 69.7, 70.3, 69.9, 69.2, 69.1, 90.7, 62.5, 62.1,…
$ weight_pounds    <dbl> 115.9631, 115.9631, 294.3171, 125.0021, 126.3249, 159.6147, 159.3942, 153.6622, 154.9850, …
$ fat              <dbl> 22, NA, NA, NA, NA, 25, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ bmi              <dbl> 22.65, 22.65, 47.54, 21.45, 21.69, 27.45, 27.38, 27.25, 27.46, 27.32, 27.04, 27.00, 28.00,…
$ is_manual_report <lgl> TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE…
$ log_id           <dbl> 1.462234e+12, 1.462320e+12, 1.460510e+12, 1.461283e+12, 1.463098e+12, 1.460938e+12, 1.4624…
# Check missing values and duplicates after cleaning

cat("\n",
    "Missing values:",
    sum(is.na(weight_logs_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(weight_logs_clean)))

 Missing values: 65 
 Duplicate values: 0

Export clean data sets

# To uncomment the following code, select all the lines and press shift + control + c on Mac


# write.csv(daily_activity_clean, 
#           "daily_activity_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(daily_sleep_clean, 
#           "daily_sleep_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(daily_sleep_clean, 
#           "hourly_activity_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(minute_sleep_clean, 
#           "minute_sleep_clean.csv",
#           row.names = FALSE)
# 
# write.csv(seconds_heartrate_clean,
#           "seconds_heartrate_clean.csv",
#           row.names = FALSE)
# 
# write.csv(weight_logs_clean , 
#           "weight_logs_clean .csv",
#           row.names = FALSE)

Analyze Phase

Exploratory Data Analysis

EDA for daily_activity_clean

str(daily_activity_clean)
tibble [862 × 15] (S3: tbl_df/tbl/data.frame)
 $ id                        : chr [1:862] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ activity_date             : Date[1:862], format: "2016-04-12" "2016-04-13" "2016-04-14" "2016-04-15" ...
 $ total_steps               : num [1:862] 13162 10735 10460 9762 12669 ...
 $ total_distance            : num [1:862] 8.5 6.97 6.74 6.28 8.16 ...
 $ tracker_distance          : num [1:862] 8.5 6.97 6.74 6.28 8.16 ...
 $ logged_activities_distance: num [1:862] 0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_distance      : num [1:862] 1.88 1.57 2.44 2.14 2.71 ...
 $ moderately_active_distance: num [1:862] 0.55 0.69 0.4 1.26 0.41 ...
 $ light_active_distance     : num [1:862] 6.06 4.71 3.91 2.83 5.04 ...
 $ sedentary_active_distance : num [1:862] 0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_minutes       : num [1:862] 25 21 30 29 36 38 42 50 28 19 ...
 $ fairly_active_minutes     : num [1:862] 13 19 11 34 10 20 16 31 12 8 ...
 $ lightly_active_minutes    : num [1:862] 328 217 181 209 221 164 233 264 205 211 ...
 $ sedentary_minutes         : num [1:862] 728 776 1218 726 773 ...
 $ calories                  : num [1:862] 1985 1797 1776 1745 1863 ...

Univariate analysis for daily_activity_clean

Numerical variables

# Subset numeric columns 
num_df <- select_if(daily_activity_clean, is.numeric)

# Identify numeric columns
colnames(num_df)
 [1] "total_steps"                "total_distance"             "tracker_distance"          
 [4] "logged_activities_distance" "very_active_distance"       "moderately_active_distance"
 [7] "light_active_distance"      "sedentary_active_distance"  "very_active_minutes"       
[10] "fairly_active_minutes"      "lightly_active_minutes"     "sedentary_minutes"         
[13] "calories"                  

# plotting all numerical variables
col_names <- colnames(num_df)
for (i in col_names) {
  suppressWarnings(print(
    ggplot(num_df, aes(num_df[[i]])) +
      geom_histogram(
        bins = 30,
        color = "black",
        fill = "gray",
        aes(y = ..density..)
      ) +
      geom_density(
        color = "blue",
        size = 1
      ) +
      xlab(i) + ylab("Count") +
      ggtitle(paste("Histogram and Density Plot of", i))
  ))
}

NA
NA
NA
NA

Observations:

  • Many variables show a right-skewed distribution: a larger number of data values are located on the left side of the curve

  • The variables total_steps, total_distance, tracker_distance have a similar distribution. We can explore their correlations later

  • Since the distributions are not normal. The median is a better indicator of central tendency for the numerical variables in these data set

  • The variable logged_activities_distance and sedentary_active_distance might not provide useful information since most of the data points are zero. It seems that the users are not logging the distance frequently

  • The following variables seem related. We will explore them further in the bivariate analysis section:

sedentary_minutes; sedentary_active_distance lightly_active_minutes; light_active_distance
fairly_active_minutes; moderately_active_distance very_active_minutes; very_active_distance

  • The variables calories and sedentary_minutes exhibit a multimodal distribution, indicating the presence of subpopulations within the data. In this dataset, gender could be a potential variable that would result in a bimodal distribution when examining histograms of calories and sedentary minutes. Unfortunately, the gender of the users is not provided, limiting our ability to confirm this hypothesis.

Categorical variables

# Subset numeric columns 

select_if(daily_activity_clean, negate(is.numeric))
NA
# Check counts by id
ggplot(data=daily_activity_clean) + 
  geom_bar(mapping = aes (x= reorder(id, id,length)))+
  xlab("id") +
  coord_flip()

  
#https://stackoverflow.com/a/9231857/15333580

#reorder(id, id, length) takes the id variable, uses itself to determine the order, and uses the length() function to calculate the values used for ordering. Essentially, this reorders the levels of the id variable based on the length of their names.
count_max_ratio <- daily_activity_clean %>%
  count(id) %>%
  rename(id = "id", count = "n") %>%
  mutate(percent_of_max = count / max(count) * 100) %>%
  arrange(desc(percent_of_max))

# Create bar graph with percentage of entries compared to maximum
ggplot(count_max_ratio, aes(x = reorder(id, percent_of_max), y = percent_of_max)) +
  geom_bar(stat = "identity") +
  xlab("ID") +
  ylab("Percentage of Maximum Count") +
  ggtitle("Count by ID and Percentage of Maximum Count") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_hline(yintercept=50, color="orange", linewidth=1)+
  geom_hline(yintercept=75, color="red", linewidth=1)+
  coord_flip()

NA
NA
# percent_of_max > 75%

percent_of_max_top_75 <- filter(count_max_ratio, percent_of_max >=75)
percent_of_max_top_75 
# percent_of_max < 75

percent_of_max_under_75 <- filter(count_max_ratio, percent_of_max < 75)
percent_of_max_under_75 
daily_activity_clean$activity_date %>% summary()
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
"2016-04-12" "2016-04-18" "2016-04-26" "2016-04-26" "2016-05-03" "2016-05-12" 
ggplot(data=daily_activity_clean , aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date") 

Observations:

  • It appears that there is missing activity data towards the end of the available period, specifically in the beginning of May
# Investigate if the missing activity data coincides with the absence of entries for certain user IDs.

ggplot(data=subset(daily_activity_clean, id %in% percent_of_max_top_75$id), aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date For IDs with Above 75% of Entries")


ggplot(data=subset(daily_activity_clean, id %in% percent_of_max_under_75$id), aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date For IDs with under 75% of Entries")

  • Users with more than 75% of data consistently report activity dates, while those with less than 75% of data show a decline in reporting starting from the end of April. The decline in Activity Date seems to be primarily due to a lack of data reporting from some users during that period.

Bivariate analysis

Correlation between numerical variables

corr <- cor(select_if(daily_activity_clean, is.numeric))

ggcorrplot(corr,
           hc.order = TRUE,
           type = "lower",
           lab = TRUE,
           colors = c("firebrick", "white", "royalblue"),
           lab_size = 4,
           lab_col = "black",
           title = "Correlation Between Numerical Variables")


#https://rdrr.io/github/microresearcher/MicroVis/man/ggcorrplot.html

sedentary_minutes; sedentary_active_distance lightly_active_minutes; light_active_distance
fairly_active_minutes; moderately_active_distance very_active_minutes; very_active_distance

# Compute correlation matrix
corr_matrix <- corr

# Set the threshold for correlation
threshold <- 0.60

# Find pairs of highly correlated variables
high_cor_pairs <- which(abs(corr_matrix) > threshold & lower.tri(corr_matrix, diag = FALSE), arr.ind = TRUE)

# Extract the variable names and correlation coefficients for the correlated pairs
variable_names <- colnames(corr_matrix)
cor_values <- as.vector(corr_matrix[high_cor_pairs])

# Create a data frame to store the correlated pairs and their correlation coefficients
cor_data <- data.frame(
  Variable1 = variable_names[high_cor_pairs[, 1]],
  Variable2 = variable_names[high_cor_pairs[, 2]],
  Correlation = cor_values
)

# Sort the correlated pairs by correlation coefficient in descending order
sorted_cor_data <- cor_data[order(-cor_data$Correlation), ]

# Remove the index
row.names(sorted_cor_data) <- NULL

# Display the sorted correlated variable pairs in the dataframe
print(sorted_cor_data)
NA
  • Total_distance, tracker_distance, and total steps are highly correlated, so we will retain only total distance and total steps as they provide similar information.

  • The following minute and distance types are correlated. Which indicates that they report different aspects of the same activity, this is time or distance:

    • lightly_active_minutes and light_active_distance (corr = 0.85)
    • fairly_active_minutes and moderately_active_distance (corr = 0.94)
    • very_active_minutes and very_active_distance (corr = 0.82)
  • There is a moderately high correlation between the time spent during very active periods and the total number of steps/total distance:

    • The correlation between very_active_minutes and total_distance is 0.68
    • The correlation between very_active_minutes and total_steps is 0.66
  • There is a moderate correlation of 0.61 between the total duration of very active minutes and the estimated daily calories consumed.

  • There is a moderate correlation of 0.62 between the total distance covered and the estimated daily calories consumed.

  • There is a moderate correlation coefficient of 0.60 between the distance covered during light activity (light_active_distance) and the total number of steps taken (total_steps).

Scatterplots of selected highly correlated variables pairs (>0.60)


# List of correlated variable pairs
correlated_pairs <- list(c("total_steps", "total_distance"), 
                         c("lightly_active_minutes", "light_active_distance"),
                         c("fairly_active_minutes", "moderately_active_distance"),
                         c("very_active_minutes", "very_active_distance"),
                         c("very_active_minutes", "total_distance"),
                         c("very_active_minutes", "total_steps"),
                         c("very_active_minutes", "calories"),
                         c("total_distance", "calories"),
                         c("light_active_distance", "total_steps"))

# Loop over each pair and create scatter plot
for (pair in correlated_pairs) {
  var1 <- pair[1]
  var2 <- pair[2]
  
  # Calculate averages
  avg_var1 <- mean(daily_activity_clean[[var1]], na.rm = TRUE)
  avg_var2 <- mean(daily_activity_clean[[var2]], na.rm = TRUE)
  
  # Create scatter plot using ggplot2 with aes()
  print(ggplot(data = daily_activity_clean, aes(x = !!sym(var1), y = !!sym(var2))) +
    geom_point() +
    geom_vline(xintercept = avg_var1, linetype = "dashed", color = "red") +
    geom_hline(yintercept = avg_var2, linetype = "dashed", color = "blue") +
    xlab(var1) + ylab(var2) +
    ggtitle(paste("Scatter Plot with Average Reference Lines of", var1, "vs", var2)))
}

User Behavior for daily activity dataset

Total steps: Total number of steps taken.

# Create a boxplot for total_steps
boxplot(daily_activity_clean$total_steps, 
        main = "Boxplot of Total Steps",
        ylab = "Total Steps")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$total_steps)
std_dev <- round(sd(daily_activity_clean$total_steps),2)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$total_steps)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)


# Steps averages by IDs
steps_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_steps = mean(total_steps), median_steps =median(total_steps), n = n())

steps_df
# Calculate percentages for the average column
at_least_10k_avg <- sum(steps_df$average_steps >= 10000) / nrow(steps_df) * 100
between_5K_10K_avg <- sum(steps_df$average_steps >= 5000 & steps_df$average_steps < 10000) / nrow(steps_df) * 100
below_5k_avg <- sum(steps_df$average_steps < 5000) / nrow(steps_df) * 100

# Calculate percentages for the median column
at_least_10k_med <- sum(steps_df$median_steps >= 10000) / nrow(steps_df) * 100
between_5K_10K_med <- sum(steps_df$median_steps >= 5000 & steps_df$median_steps < 10000) / nrow(steps_df) * 100
below_5k_med <- sum(steps_df$median_steps < 5000) / nrow(steps_df) * 100

# Create a data frame for the steps categories
percentage_steps_df<- data.frame(
  Category = c("Below 5,000", "Between 5,000 and 10,000", "At least 10,000"),
  Percentage_Average = round(c(below_5k_avg, between_5K_10K_avg, at_least_10k_avg)),
  Percentage_Median = round(c(below_5k_med, between_5K_10K_med, at_least_10k_med)))
percentage_steps_df
NA
# Convert Category to a factor with custom factor levels
percentage_steps_df$Category <- factor(percentage_steps_df$Category, levels = c("Below 5,000", "Between 5,000 and 10,000", "At least 10,000"))

# Create a bar plot using ggplot
ggplot(percentage_steps_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "blue") +
  labs(x = "Average Total Steps", y = "Percentage of Users", title = "58% of Users Average 5,000-10,000 Step Daily",subtitle = "Only 21% Achieve the 10,000-Step Goal") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +  theme_minimal() + theme(panel.grid = element_blank())

NA
NA

Total Distance: Total kilometers tracked.

# Create a boxplot for total_distance
boxplot(daily_activity_clean$total_distance, 
        main = "Boxplot of Total Distance",
        ylab = "Total Distance")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$total_distance)
std_dev <- sd(daily_activity_clean$total_distance)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$total_distance)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2), 
                      "\nStandard Deviation:", round(std_dev, 2), 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n")


# Total distance by IDs
t_distance_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_t_distance = mean(total_distance ), median_t_distance =median(total_distance), n = n())

t_distance_df
# Calculate percentages for the average column
at_least_10_avg<- sum(t_distance_df$average_t_distance>= 10) / nrow(t_distance_df) * 100
between_5_10_avg <- sum(t_distance_df$average_t_distance >= 5 & t_distance_df$average_t_distance < 10) / nrow(t_distance_df) * 100
below_5_avg <- sum(t_distance_df$average_t_distance < 5) / nrow(t_distance_df) * 100


# Create a data frame for the distance categories
percentage_t_distance_df<- data.frame(
  Category = c("Below 5 km", "Between 5 and 10 km", "At least 10 km"),
  Percentage_Average = round(c(below_5_avg, between_5_10_avg , at_least_10_avg)))
percentage_t_distance_df



# Convert Category to a factor with custom factor levels
percentage_t_distance_df$Category <- factor(percentage_t_distance_df$Category, levels = c("Below 5 km", "Between 5 and 10 km", "At least 10 km"))
# Create a bar plot using ggplot
ggplot(percentage_t_distance_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "pink") +
  labs(x = "Average Total Distance", y = "Percentage of Users", title = "55% of Users Average 5-10 Kilometers Daily",subtitle = "10,000 steps is approximately equal to covering 5 miles (or 8 kilometers)") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +  theme_minimal() +theme(panel.grid = element_blank())

Sedentary Minutes: Total minutes spent in sedentary activity.

# Create a boxplot for sedentary_minutes
boxplot(daily_activity_clean$sedentary_minutes,
        main = "Boxplot of Sedentary Minutes",
        ylab = "Sedentary Minutes")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$sedentary_minutes)
std_dev <- sd(daily_activity_clean$sedentary_minutes)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$sedentary_minutes)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

NA
NA
  • These are high values for sedentary minutes. For instance, 1020 minutes is equivalent to 17 hours, and 1400 minutes is equivalent to 24 hours. After performing a quick search, it seems that the Fitbit uses 1400 as default for sedentary minutes when the device is not worn and it includes the sleeping time. SedentaryMinutes is total minutes spent in sedentary activity according to the data dictionary. See meta data section. Therefore, we need to substract the times sleeping to obtain an more accurate estimate of daily sedentary minutes.

Sleep time is not considered sedentary time, so it was removed to determine the waking day and to allow the proportion of the day spent sedentary to be calculated

# Check sedentary_minutes stats
daily_activity_clean$sedentary_minutes %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0   721.2  1020.5   955.2  1189.0  1440.0 
outliers
[1]  2 13  0
# Count entries where sedentary minutes equal 1440
count_1440 <- sum(daily_activity_clean$sedentary_minutes == 1440)

# Output the count
count_1440
[1] 7
# Remove rows with sedentary minutes equal to the default value (1440) and outliers

daily_activity_clean <- filter(daily_activity_clean, !(sedentary_minutes %in% c(0, 2, 13, 1440)))


# Rename the column
daily_sleep_clean <- rename(daily_sleep_clean, activity_date = sleep_day)

# Join the datasets
joined_activity_sleep <- inner_join(daily_activity_clean, daily_sleep_clean, by = c("id", "activity_date"))


# Check missing values and duplicates
cat(
  "\n",
  "Missing values:",
  sum(is.na(joined_activity_sleep )),
  "\n",
  "Duplicate values:",
  sum(duplicated(joined_activity_sleep )),
  "\n",
  "Unique Ids:",
  n_distinct(joined_activity_sleep $id)
)

 Missing values: 0 
 Duplicate values: 0 
 Unique Ids: 24
# Create a derived column for sedentary minutes that does not include sleep time
joined_activity_sleep <- joined_activity_sleep %>%
  mutate(
    sedentary_min_awake = sedentary_minutes - total_minutes_asleep,
    sedentary_hours_awake = sedentary_min_awake / 60,
    sedentary_percentage_diff = (sedentary_minutes - sedentary_min_awake) / sedentary_minutes * 100
  )

# Let us check the percentage difference of sedentary_minutes and the new column "sedentary_min_awake

# Create a boxplot for sedentary_percentage_diff
boxplot(joined_activity_sleep$sedentary_percentage_diff,
        main = "Boxplot of Sedentary Percentage Difference",
        ylab = "Sedentary Percentage Difference")

# Calculate the median and standard deviation
median_value <- median(joined_activity_sleep$sedentary_percentage_diff)
std_dev <- sd(joined_activity_sleep$sedentary_percentage_diff)

# Identify outliers
outliers <- boxplot.stats(joined_activity_sleep$sedentary_percentage_diff)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

  • The sedentary percentage difference has a median value of 59.95%, indicating a significant distinction between sedentary_minutes and sedentary_min_awake. This suggest that the original column “sedentary_minutes” included the time asleep.
# Create a boxplot for sedentary_min_awake
boxplot(joined_activity_sleep$sedentary_min_awake,
        main = "Boxplot of Sedentary Minutes Awake",
        ylab = "Sedentary Minutes Awake")

# Calculate the median and standard deviation
median_value <- median(joined_activity_sleep$sedentary_min_awake)
std_dev <- sd(joined_activity_sleep$sedentary_min_awake)

# Identify outliers
outliers <- boxplot.stats(joined_activity_sleep$sedentary_min_awake)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

  • Observation: There appears to be an inconsistency in the data. The sedentary_minutes value is smaller than the total_minutes_asleep value, which is unexpected.
# Count the number of cases where sedentary_minutes is smaller than total_minutes_asleep
count <- sum(joined_activity_sleep$sedentary_minutes < joined_activity_sleep$total_minutes_asleep)

# Print the count
count
[1] 42
# Subset the dataset
subset_data <- joined_activity_sleep[joined_activity_sleep$sedentary_minutes < joined_activity_sleep$total_minutes_asleep, ]

# View the subsetted data
subset_data
NA
# Check column names of the subsetted data
subset_data %>%
select(sedentary_minutes, total_minutes_asleep, sedentary_min_awake, calories,id, activity_date, total_steps, total_distance, very_active_minutes )
dim(subset_data)
[1] 42 21
dim(joined_activity_sleep)
[1] 408  21
# Use anti_join() to return a new dataset that includes all rows from the first dataset except for the rows that have a match in the second dataset.
 clean_subset<- anti_join(joined_activity_sleep, subset_data)
Joining with `by = join_by(id, activity_date, total_steps, total_distance, tracker_distance, logged_activities_distance, very_active_distance, moderately_active_distance, light_active_distance, sedentary_active_distance, very_active_minutes, fairly_active_minutes, lightly_active_minutes, sedentary_minutes, calories, total_sleep_records, total_minutes_asleep, total_time_in_bed, sedentary_min_awake, sedentary_hours_awake, sedentary_percentage_diff)`
dim(clean_subset)
[1] 366  21
# Create a boxplot for sedentary_min_awake
boxplot(clean_subset$sedentary_min_awake,
        main = "Boxplot of Sedentary Minutes Awake",
        ylab = "Sedentary Minutes Awake")

# Calculate the median and standard deviation
median_value <- median(clean_subset$sedentary_min_awake)
std_dev <- sd(clean_subset$sedentary_min_awake)

# Identify outliers
outliers <- boxplot.stats(clean_subset$sedentary_min_awake)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

Observation: By eliminating negative values from “sedentary_min_awake,” the resulting values now reflect a more realistic scenario.

# Total sedentary minutes awake by IDs
t_sedentary_df <- clean_subset %>%
  group_by(id) %>%
  summarise(average_sedentary_min_awake = mean(sedentary_min_awake),
            median_sedentary_min_awake = median(sedentary_min_awake), n = n())

t_sedentary_df
NA
dataset <- t_sedentary_df
column <- "average_sedentary_min_awake"
new_categories <- c("Below 200 minutes", "Between 200 and 400 minutes", "At least 400 minutes")

# Calculate percentages for the average column
below_200_avg <- sum(dataset[[column]] < 200) / nrow(dataset) * 100
between_200_400_avg <- sum(dataset[[column]] >= 200 & dataset[[column]] <= 400) / nrow(dataset) * 100
at_least_400_avg <- sum(dataset[[column]] >= 400) / nrow(dataset) * 100

# Create a data frame for the categories
percentage_sedentary_awake_df <- data.frame(
  Category = new_categories,
  Percentage_Average = round(c(below_200_avg, between_200_400_avg, at_least_400_avg))
)

# Convert Category to a factor with custom factor levels
percentage_sedentary_awake_df$Category <- factor(percentage_sedentary_awake_df$Category, levels = new_categories)

percentage_sedentary_awake_df
NA
NA
NA
# Create a bar plot using ggplot
ggplot(percentage_sedentary_awake_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "gray") +
  labs(x = "Average Total Sedentary Min Awake", y = "Percentage of Users", 
       title = "48% of Users Have an Average of at Least 400 Daily Sedentary Minutes While Awake",
       subtitle = "200 Minutes are 3 hours and 20 minutes; 400 min are 6 hours and 40 min") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), plot.title = element_text(size = 12), plot.subtitle = element_text(size = 10))

In a representative sample of U.S. adults, over two-thirds spent 6 + hours/day sitting, and more than half did not meet the recommended 150 min/week of physical activity. The study discovered that prolonged sitting for 6+ hours/day was associated with higher body fat percentages. While exceeding 150 min/week of physical activity was linked to lower body fat percentages, achieving recommended activity levels may not fully offset the increased body fat from prolonged sitting.

Jingwen Liao, Min Hu, Kellie Imm, Clifton J. Holmes, Jie Zhu, Chao Cao, Lin Yang. Association of daily sitting time and leisure-time physical activity with body fat among U.S. adults. Journal of Sport and Health Science, 2022. ISSN 2095-2546. https://doi.org/10.1016/j.jshs.2022.10.001. (https://www.sciencedirect.com/science/article/pii/S2095254622001016)

Calories:Total estimated energy expenditure (in kilocalories).

# Create a boxplot for calories
boxplot(daily_activity_clean$calories, 
        main = "Boxplot of Calories",
        ylab = "Calories")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$calories)
std_dev <- round(sd(daily_activity_clean$calories),2)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$calories)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)

outliers
[1] 4552 4392 4501 4546 4900 4547 4398
# Calories averages by IDs
calories_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_calories = mean(calories), median_calories = median(calories))

calories_df
NA
# Calculate percentages for the average column
below_1600_avg <- sum(calories_df$average_calories < 1600) / nrow(calories_df) * 100
between_1600_2200_avg <- sum(calories_df$average_calories >= 1600 & calories_df$average_calories < 2200) / nrow(calories_df) * 100
between_2200_3000_avg <- sum(calories_df$average_calories >= 2200 & calories_df$average_calories < 3000) / nrow(calories_df) * 100
at_least_3000_avg <- sum(calories_df$average_calories >= 3000) / nrow(calories_df) * 100

# Calculate percentages for the median column
below_1600_med <- sum(calories_df$median_calories < 1600) / nrow(calories_df) * 100
between_1600_2200_med <- sum(calories_df$median_calories >= 1600 & calories_df$median_calories < 2200) / nrow(calories_df) * 100
between_2200_3000_med <- sum(calories_df$median_calories >= 2200 & calories_df$median_calories < 3000) / nrow(calories_df) * 100
at_least_3000_med <- sum(calories_df$median_calories >= 3000) / nrow(calories_df) * 100

# Create a data frame for the calories categories
percentage_calories_df <- data.frame(
  Category = c("Below 1,600", "Between 1,600 and 2,200", "Between 2,200 and 3,000", "At least 3,000"),
  Percentage_Average = round(c(below_1600_avg, between_1600_2200_avg, between_2200_3000_avg, at_least_3000_avg)),
  Percentage_Median = round(c(below_1600_med, between_1600_2200_med, between_2200_3000_med, at_least_3000_med))
)

# Convert Category to a factor with custom factor levels
percentage_calories_df$Category <- factor(percentage_calories_df$Category, levels = c("Below 1,600", "Between 1,600 and 2,200", "Between 2,200 and 3,000", "At least 3,000"))

percentage_calories_df
NA
# Create a bar plot using ggplot
ggplot(percentage_calories_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "red") +
  labs(x = "Calorie Categories", y = "Percentage of Users", 
       title = "42% of Users Have an Average Daily Calorie Expenditure Between 1,600 and 2,200.",
       subtitle = "Most females require 1,600 to 2,200 calories per day, as per the Dietary Guidelines for Americans, 2020-2025") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), 
        plot.title = element_text(size = 12), 
        plot.subtitle = element_text(size = 10))

“Females ages 19 through 30 require about 1,800 to 2,400 calories a day. Males in this age group have higher calorie needs of about 2,400 to 3,000 a day. Calorie needs for adults ages 31 through 59 are generally lower; most females require about 1,600 to 2,200 calories a day and males require about 2,200 to 3,000 calories a day.”

U.S. Department of Agriculture and U.S. Department of Health and Human Services. Dietary Guidelines for Americans, 2020-2025. 9th Edition. December 2020. Available at DietaryGuidelines.gov/

Intensity Minutes: Time spent in one of four intensity categories.

  • VeryActiveMinutes: Total minutes spent in very active activity

  • FairlyActiveMinutes: Total minutes spent in moderate activity

  • LightlyActiveMinutes: Total minutes spent in light activity

  • SedentaryMinutes: Total minutes spent in sedentary activity

activity_minutes_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(
    average_very_active_minutes = mean(very_active_minutes),
    average_fairly_active_minutes = mean(fairly_active_minutes),
    average_lightly_active_minutes = mean(lightly_active_minutes),
    average_sedentary_minutes = mean(sedentary_minutes)
  )

activity_minutes_df
NA
NA

# Define the custom order of legend items
custom_order <- c( "Very Active", "Fairly Active", "Lightly Active", "Sedentary")

# Create the stacked bar plot
ggplot(activity_minutes_df, aes(y = id)) +
  geom_bar(aes(x = average_sedentary_minutes, fill = "Sedentary"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_lightly_active_minutes, fill = "Lightly Active"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_fairly_active_minutes, fill = "Fairly Active"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_very_active_minutes, fill = "Very Active"), stat = "identity", width = 0.5) +
  xlab("Minutes") +
  ylab("ID") +
  ggtitle("Average Activity Minutes by ID") +
  scale_fill_manual(name = "", values = c("Very Active" = "red", "Fairly Active" = "orange", "Lightly Active" = "lightgreen", "Sedentary" = "lightblue"), breaks = custom_order) +
  theme_minimal() +
  theme(legend.position = "bottom", panel.grid = element_blank()) 

NA
NA
NA
NA
NA
NA
NA
NA
# Calculate the average for each column
averages <- colMeans(activity_minutes_df[, c("average_very_active_minutes",
                                             "average_fairly_active_minutes",
                                             "average_lightly_active_minutes",
                                             "average_sedentary_minutes")])

# Calculate the total average
total_average <- sum(averages)

# Calculate the proportions
proportions <- averages / total_average

# Create the new dataframe with modified row names
overall_average_df<- data.frame(Average = averages,
                     Percentage = proportions * 100)

# Modify the row names
row_names <- c("Very Active", "Fairly Active", "Lightly Active", "Sedentary")
row.names(overall_average_df) <- row_names

# Print the new dataframe
overall_average_df
NA
NA

ggplot(overall_average_df, aes(x = Percentage, y = reorder(row.names(overall_average_df), Percentage), fill = row.names(overall_average_df))) +
  geom_bar(stat = "identity", width = 0.7, show.legend = FALSE) +
  geom_text(aes(label = paste0(round(Percentage), "%")), hjust = -0.2, color = "black", size = 4) +
  ylab("Minutes Intensity") +
  xlab("Percentage") +
  ggtitle("Users' Overall Average Intensity Minutes Consist Primarily of Sedentary and Lightly Active Time") +
  scale_fill_manual(values = c("Very Active" = "red", "Fairly Active" = "orange", "Lightly Active" = "lightgreen", "Sedentary" = "lightblue")) +
 scale_x_continuous(labels = NULL) +
  theme_minimal() +
  theme(legend.position = "none", panel.grid = element_blank(), axis.text.y = element_text(size = 10))

NA
NA

“Analyzing each individual’s average calorie intake can provide insights into their individual dietary habits and patterns. By comparing the individual averages to the overall average, you can identify individuals who consume more or fewer calories compared to the group average. This comparison can help in understanding variations in calorie intake and potential factors influencing individual differences.”

# Define the custom order of legend items

custom_order <- c("Very Active", "Fairly Active", "Lightly Active", "Sedentary")


# Create the stacked horizontal bar chart
ggplot(overall_average_df, aes(x = Percentage, y = factor(1), fill = factor(row.names(overall_average_df), levels = custom_order))) +
  geom_bar(stat = "identity", width = 0.7) +
  xlab("Percentage") +
  ylab("") +
  ggtitle("Users' Overall Average Intensity Minutes Consist Primarily of Sedentary and Lightly Active Time") +
  scale_fill_manual(
    name = "",
    values = c(
      "Very Active" = "red",
      "Fairly Active" = "orange",
      "Lightly Active" = "lightgreen",
      "Sedentary" = "lightblue"
    ),
    breaks = custom_order
  ) +
  guides(fill = guide_legend(reverse = TRUE)) +  # Reverse the order of the legend
  theme_minimal() +
  theme(legend.position = "top", 
        panel.grid = element_blank(),
         axis.text.y = element_blank(),  # Remove the y-axis text
         plot.title = element_text(size = 12, margin = margin(b = 20))) + # Adjust the title size and margin
  geom_vline(xintercept = 97, color = "black", linetype = "dashed") +
  annotate("text", x = 97, y = 1, label = "   97%", vjust = -5.5, hjust = 0.1)

NA
NA

These indicators provide insights into activity levels, sedentary behavior, and calorie burn. They can help track progress, set goals, and evaluate user behavior over time. Remember to consider the specific context and goals of your analysis to select and customize the most relevant KPIs for your use case. The context I will use is the guidelines for physical activity and diet for Americans:

EDA for daily_sleep_clean

str(daily_sleep_clean)
tibble [410 × 5] (S3: tbl_df/tbl/data.frame)
 $ id                  : chr [1:410] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ activity_date       : Date[1:410], format: "2016-04-12" "2016-04-13" "2016-04-15" "2016-04-16" ...
 $ total_sleep_records : num [1:410] 1 2 1 2 1 1 1 1 1 1 ...
 $ total_minutes_asleep: num [1:410] 327 384 412 340 700 304 360 325 361 430 ...
 $ total_time_in_bed   : num [1:410] 346 407 442 367 712 320 377 364 384 449 ...
  • activity_date (sleep_day): Date on which the sleep event started.
  • total_sleep_records: Number of recorded sleep periods for that day. Includes naps > 60 min.
  • total_minutes_asleep: Total number of minutes classified as being “asleep”.
  • total_time_in_bed: Total minutes spent in bed, including asleep, restless, and awake, that occurred during a defined sleep record.
#Sanity check: Verify that the value of total_time_in_bed is greater than total_minutes_asleep, as we would expect.
daily_sleep_clean[daily_sleep_clean$total_time_in_bed < daily_sleep_clean$total_minutes_asleep,]

Univariate analysis


numerical_cols <- daily_sleep_clean%>%
  select_if(is.numeric)

# plotting all numerical variables
col_names <- colnames(numerical_cols )
for (i in col_names) {
  suppressWarnings(print(
    ggplot(numerical_cols , aes(numerical_cols [[i]])) +
      geom_histogram(
        bins = 30,
        color = "black",
        fill = "gray",
        aes(y = ..density..)
      ) +
      geom_density(
        color = "blue",
        size = 1
      ) +
      xlab(i) + ylab("Count") +
      ggtitle(paste("Histogram with Density Plot of", i))
  ))
}

Bivariate analysis

Correlation between numerical variables

# Correlation between numerical variables
corr <- cor(select_if(daily_sleep_clean, is.numeric))

ggcorrplot(corr,
           hc.order = TRUE,
           type = "lower",
           lab = TRUE,
           colors = c("firebrick", "white", "royalblue"),
           lab_size = 4,
           lab_col = "black",
           title = "Correlation Between Numerical Variables")

NA
NA

Scatterplots of total_minutes_asleep vs total_time_in_bed


ggplot(data = daily_sleep_clean, aes(x = total_minutes_asleep, y = total_time_in_bed)) +
  geom_point()

User Behavior for daily sleep dataset


#library(scales)
frequency_table <- as.data.frame(table(daily_sleep_clean$total_sleep_records))
frequency_table$Percentage <- frequency_table$Freq / sum(frequency_table$Freq) * 100

ggplot(data = frequency_table, aes(x = Var1, y = Freq)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = paste(Freq, " (", percent(Percentage / 100), ")", sep = "")),
            hjust = 0.5,  vjust = -0.4, color = "black") +
  labs(x = "Total Sleep Records", y = "Frequency",
       title = "Uncommon Napping: 89% of Sleep Records Indicate a Singular Sleep Period.",
       subtitle = "Includes naps > 60 min.")+
  theme_minimal() +
  theme(panel.grid = element_blank(),
        plot.title = element_text(size = 12),
        plot.subtitle = element_text(size = 10, margin = margin(b = 20)))

NA
NA
NA

Total Minutes Asleep

# Create a boxplot for total_minutes_asleep
boxplot(daily_sleep_clean$total_minutes_asleep, 
        main = "Boxplot of Total Minutes Asleep",
        ylab = "Total Minutes Asleep")

# Calculate the median and standard deviation
median_value <- median(daily_sleep_clean$total_minutes_asleep)
std_dev <- round(sd(daily_sleep_clean$total_minutes_asleep), 2)

# Identify outliers
outliers <- boxplot.stats(daily_sleep_clean$total_minutes_asleep)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)

# Sleep duration averages by IDs with standard deviation and count (n)
sleep_df <- daily_sleep_clean %>%
  group_by(id) %>%
  summarise(average_sleep_minutes = mean(total_minutes_asleep),
            standard_deviation_sleep_minutes = sd(total_minutes_asleep),
            n = n())

sleep_df
NA
NA
NA
# Drop ID "2320127002" due to insufficient data for computing mean and standard deviation.
sleep_df <- sleep_df %>% 
filter(id != "2320127002")
sleep_df 
# Calculate percentages for the average column
below_6_hours <- sum(sleep_df$average_sleep_minutes < 360) / nrow(sleep_df) * 100
between_6_7_hours <- sum(sleep_df$average_sleep_minutes >= 360 & sleep_df$average_sleep_minutes < 420) / nrow(sleep_df) * 100
at_least_7_hours <- sum(sleep_df$average_sleep_minutes >= 420) / nrow(sleep_df) * 100

# Create a data frame for the sleep duration categories
percentage_sleep_df <- data.frame(
  Category = c("Below 6 hours", "Between 6 and 7 hours", "At least 7 hours"),
  Percentage_Average = round(c(below_6_hours, between_6_7_hours, at_least_7_hours))
)

# Convert Category to a factor with custom factor levels
percentage_sleep_df$Category <- factor(percentage_sleep_df$Category, levels = c("Below 6 hours", "Between 6 and 7 hours", "At least 7 hours"))

percentage_sleep_df


ggplot(percentage_sleep_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "purple") +
  labs(x = "Average Sleep Duration", y = "Percentage of Users", 
       title = "52% of Users Get Less Than 7 Hours of Sleep on Average Daily") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), plot.title = element_text(size = 12), plot.subtitle = element_text(size = 10))

Sleep Duration Consistency

#Error bars

# Convert average_sleep_minutes and standard_deviation_sleep_minutes to hours
sleep_df$average_sleep_hours <- sleep_df$average_sleep_minutes / 60
sleep_df$standard_deviation_sleep_hours <- sleep_df$standard_deviation_sleep_minutes / 60


# Create a bar plot for each 'id' with error bars representing standard deviation
ggplot(sleep_df, aes(x = id, y = average_sleep_hours)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "black") +
  geom_errorbar(aes(ymin = average_sleep_hours - standard_deviation_sleep_minutes / 60,
                    ymax = average_sleep_hours + standard_deviation_sleep_minutes / 60),
                width = 0.2, position = position_dodge(0.9), color = "black") +
  labs(x = "ID", y = "Average Sleep Duration (hours)",
       title = "Sleep Consistency: Average Sleep Duration with Error Bars",
       subtitle = "Error bars represent the standard deviation around the mean.") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  geom_hline(yintercept = 7, linetype = "dashed", color = "red") +
  scale_y_continuous(breaks = seq(0, 12, 1))  # Adjust the range as needed

# Calculate sleep duration averages and standard deviations in hours
sleep_df <- daily_sleep_clean %>%
  group_by(id) %>%
  summarise(n = n(),
            average_sleep_hours = mean(total_minutes_asleep) / 60,       # Convert minutes to hours
            average_time_in_bed_hours = mean(total_time_in_bed) / 60,   
            standard_deviation_sleep_hours = sd(total_minutes_asleep) / 60,     
            standard_deviation_time_in_bed_hours = sd(total_time_in_bed) / 60,    
           ) %>%
  mutate(time_difference_hours = average_time_in_bed_hours - average_sleep_hours,  # Calculate the time difference in hours
         average_awake_in_bed_hours = time_difference_hours,  # Rename column "awake_in_bed"
         sd_awake_in_bed_hours = sd(time_difference_hours))  # Calculate SD for "awake_in_bed" in hours


sleep_df
NA
NA
NA
# Drop ID "2320127002" due to insufficient data for computing mean and standard deviation.
sleep_df <- sleep_df %>% 
filter(id != "2320127002")
dim(sleep_df)
[1] 23  9

#next: understand this function

create_boxplots_in_one_output <- function(data_frame, columns_to_analyze, decimal_places = 2) {
  num_columns <- length(columns_to_analyze)
  num_rows <- ceiling(num_columns / 2)
  
  par(mfrow = c(num_rows, 2))  # Set the plotting layout
  
  for (i in 1:num_columns) {
    column_name <- columns_to_analyze[i]
    boxplot(data_frame[[column_name]],
            ylab = column_name)
    
    median_value <- median(data_frame[[column_name]])
    std_dev <- round(sd(data_frame[[column_name]]), decimal_places)
    outliers <- boxplot.stats(data_frame[[column_name]])$out
    num_outliers <- length(outliers)
    
    legend_label <- paste("Median:", round(median_value, decimal_places),
                          "\nSD:", std_dev,
                          "\nOutliers:", num_outliers)
    
    legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.75)
  }
  
  par(mfrow = c(1, 1))  # Reset the plotting layout to default
}

# Columns to analyze
columns_to_analyze <- c("average_sleep_hours", "average_awake_in_bed_hours")

# Call the function to create boxplots in one output
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

# Columns to analyze
columns_to_analyze <- c("standard_deviation_sleep_hours", "sd_awake_in_bed_hours")

# Call the function
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

#Columns with outliers to remove
columns_with_outliers <- c("average_sleep_hours", "average_awake_in_bed_hours", "standard_deviation_sleep_hours")

# Function to remove outliers from a column
remove_outliers <- function(data, column_name) {
  outlier_bounds <- boxplot.stats(data[[column_name]])$out
  data_no_outliers<- data[!(data[[column_name]] %in% outlier_bounds), ]
  return(data_no_outliers)
}

# Loop through each column and remove outliers
for (col in columns_with_outliers) {
  sleep_df <- remove_outliers(sleep_df, col)
}
sleep_df
# Check if outliers were removed
columns_to_analyze <- c("average_sleep_hours", "average_awake_in_bed_hours", "standard_deviation_sleep_hours")


# Call the function to create boxplots in one output
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

#Let us divide the users into irregular sleepers and regular sleepers. We will use the 75th percentile as the threshold to determine irregular sleepers. The rest will be considered regular sleepers.

# Define the Threshold (e.g., using the 75th percentile)
threshold <- quantile(sleep_df$standard_deviation_sleep_hours, 0.75)

# Create a new column "sleeper_type" based on the threshold
sleep_df$sleeper_type <- ifelse(sleep_df$standard_deviation_sleep_hours > threshold, "irregular", "regular")
sleep_df
# sleep_type counts
table(sleep_df$sleeper_type)

irregular   regular 
        4        13 
sleep_df

color_options <- c("#E69F00", "#0072B2") # Blue: "#0072B2", Orange: "#E69F00"

# Function to create the violin plot for a given y-axis column
create_violin_plot <- function(data, x_axis_col, y_axis_col) {
  ggplot(data, aes_string(x = x_axis_col, y = y_axis_col, fill = x_axis_col)) +
    geom_violin(scale = "width", draw_quantiles = c(0.25, 0.5, 0.75), trim = FALSE) +
    geom_boxplot(width = 0.1, fill = "white", color = "black") +
    labs(x = "Sleeper Type", y = y_axis_col, title = paste("Comparison",x_axis_col,"for", y_axis_col)) +
    scale_fill_manual(values = color_options) +
    theme_minimal()
}
# Call the function to create the violin plots for each column
for (col in c("average_sleep_hours", "standard_deviation_sleep_hours", "average_awake_in_bed_hours","sd_awake_in_bed_hours")) {
  plot <- create_violin_plot(sleep_df, "sleeper_type", col)
  print(plot)
}

Observations:

  • Regular sleepers tend to have higher median average sleep hours compared to irregular sleepers.This suggests that individuals classified as regular sleepers are likely getting more sleep on average than those categorized as irregular sleepers.

  • Additionally, the spread of the “average_sleep_hours” for irregular sleepers appears to be wider, indicating more variability in their sleep duration. In contrast, the violin plot for regular sleepers shows a narrower spread, suggesting that their sleep duration is more consistent.

  • Regular sleepers exhibit a slightly higher median average awake-in-bed duration compared to irregular sleepers.

Summary: Regular sleepers get more sleep on average, have a more consistent sleep duration, and slightly higher median awake-in-bed duration than irregular sleepers.

  • The mean of ‘standard_deviation_sleep_hours’ can provide valuable insights into the sleep consistency of the users in the dataset. Here’s what the mean of the standard deviation can tell you about the users:

Average Sleep Variability: The mean of ‘standard_deviation_sleep_hours’ gives you an average measure of how much the users’ sleep durations vary from their respective average sleep duration. A higher mean standard deviation implies more inconsistency in sleep patterns among users.

Sleep Schedule Stability: Users with a higher mean standard deviation may have irregular sleep schedules, with varying sleep durations on different days, indicating potential issues with sleep consistency.

Sleep Quality: In some cases, a higher mean standard deviation might indicate poor sleep quality, as unstable or fragmented sleep patterns can lead to less restorative sleep.

Sleep Regularity: A lower mean standard deviation generally suggests that users have more consistent sleep patterns and adhere to a more regular sleep schedule.

Health Indicators: Sleep consistency can be linked to overall health and well-being. Users with a lower mean standard deviation might experience better health outcomes, as consistent sleep is associated with improved physical and mental health.

Potential Sleep Disorders: Users with high standard deviation values may have erratic sleep patterns, which could be indicative of sleep disorders or disturbances that require further investigation.

Sleep Habits: Understanding the mean standard deviation can offer insights into sleep habits and behaviors. For example, users with high variability might have irregular bedtime routines or frequent disruptions during sleep.

Stress Levels: Inconsistent sleep patterns can be associated with higher stress levels, so a higher mean standard deviation might hint at increased stress or anxiety levels among some users.



# # Assuming sleep_df contains 'id', 'average_sleep_minutes', and 'standard_deviation_sleep_minutes'
# 
# # Calculate confidence intervals
# sleep_df <- transform(sleep_df, 
#                       lower_ci = average_sleep_minutes - 1.96 * standard_deviation_sleep_minutes / sqrt(n),
#                       upper_ci = average_sleep_minutes + 1.96 * standard_deviation_sleep_minutes / sqrt(n))
# 
# # Plot the bar plot with confidence intervals
# ggplot(sleep_df, aes(x = id, y = average_sleep_minutes)) +
#   geom_bar(stat = "identity", fill = "skyblue", color = "black") +
#   geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci),
#                 width = 0.2, position = position_dodge(0.9), color = "black") +
#   labs(x = "ID", y = "Average Sleep Duration (minutes)",
#        title = "Sleep Consistency: Average Sleep Duration with Confidence Intervals",
#        subtitle = "Error bars represent the 95% confidence intervals around the mean.") +
#   theme_minimal() +
#   theme(axis.text.x = element_text(angle = 45, hjust = 1))

next:

I need to create categories for variability

Create a sleep consistency score that combines factors like regularity of sleep duration and bedtime to get an overall measure of sleep pattern consistency.

Step 5: Create a sleep consistency score You can define your own formula based on various factors like regularity of sleep duration, bedtime, etc. For example, you could use a weighted average of standard deviation, correlation, and other metrics.

do same as I did for steps for minutes as_sleep

Adult 18-60 years 7 or more hours per night check for sleeo consistency Be consistent. Go to bed at the same time each night and get up at the same time each morning, including on the weekends.

To analyze sleep user behavior in the provided dataset, you can consider the following key performance indicators (KPIs):

  1. Sleep Efficiency: Calculate the percentage of time users spend asleep compared to the total time in bed. This can be computed as (total_sleep_records_asleep / total_time_in_bed) * 100. Higher sleep efficiency indicates more consolidated and restful sleep.

  2. Average Sleep Duration: Calculate the average duration of sleep for each user by dividing the total sleep records asleep by the total number of sleep records. This provides an indication of the typical length of sleep sessions.

  3. Sleep Quality: Assess sleep quality by considering factors such as wakefulness during the night, sleep disturbances, or the number of awakenings. You can evaluate sleep quality by examining patterns in the sleep records, identifying disruptions, or using subjective ratings if available.

  4. Sleep Latency: Measure the time it takes for users to fall asleep after getting into bed. This can be calculated as the average time from going to bed to the onset of sleep.

  5. Sleep Consistency: Analyze the consistency of sleep patterns by examining the regularity of sleep duration and bedtime. Irregular sleep patterns may indicate disrupted or inconsistent sleep routines.

  6. Sleep Architecture: Assess the distribution of sleep stages, such as deep sleep, light sleep, and REM sleep. This analysis requires additional sleep stage data beyond the variables provided in the dataset.

  7. Sleep Variability: Analyze the variation in sleep duration and bedtime across different days or weeks. Higher variability may indicate irregular sleep patterns or disrupted sleep schedules.

These KPIs can provide insights into sleep behavior and patterns, helping to identify areas for improvement or potential sleep issues. Remember to consider the limitations of the dataset and the specific context of the sleep data being analyzed.

EDA for hourly_activity_clean

EDA for seconds_heartrate_clean

EDA for weight_logs_clean

  • Weight Reporting Behavior: Assess the reporting behavior of users using the IsManualReport variable. Calculate the percentage of manual weight reports compared to total weight reports to determine how actively users are providing weight updates. This can indicate user engagement and motivation to track their weight.

  • Data Completeness: Analyze the completeness and quality of data using variables such as LogId. Assess if there are missing or erroneous data points that may impact the analysis. Exclude or handle such data points appropriately to ensure accurate insights.

  • BMI distribution (percentage above normal BMI)

  • Weight Trends: Analyze the trends in weight over time (Date) to understand if users are experiencing weight loss, gain, or stability. Plotting weight (WeightKg or WeightPounds) against time can reveal patterns, fluctuations, or significant changes in weight. You can also calculate descriptive statistics such as average weight, standard deviation, or rate of weight change to gain insights into user behavior.

Insights and recommendations

https://www.cdc.gov/mmwr/volumes/68/wr/mm6823a1.htm

file:///Users/vivianbarros/Desktop/Physical_Activity_Guidelines_2nd_edition.pdf

Appendix

https://stackoverflow.com/questions/13035834/plot-every-column-in-a-data-frame-as-a-histogram-on-one-page-using-ggplot

https://stackoverflow.com/questions/13035834/plot-every-column-in-a-data-frame-as-a-histogram-on-one-page-using-ggplot

Another source

#paper https://dl.acm.org/doi/pdf/10.1145/3339825.3394926

kaggle https://www.kaggle.com/datasets/arashnic/fitbit/discussion/313589?page=2

this is it:

https://www.cdc.gov/physicalactivity/data/inactivity-prevalence-maps/index.html#Race-Ethnicity

https://www.bls.gov/tus/data/datafiles-0321.htm

https://www.cdc.gov/nchs/products/databriefs/db443.htm

https://www.cdc.gov/nchs/products/databriefs/db443.htm

Reference:

Categorical, ordinal, interval, and ratio variables : https://www.graphpad.com/guides/prism/latest/statistics/the_different_kinds_of_variabl.htm

Add density line to histogram: https://r-coder.com/density-plot-r

LS0tCnRpdGxlOiAiQmVsbGFiZWF0IENhc2Ugc3R1ZHkgZHJhZnQxIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMgUHJlcGFyZSBhbmQgUHJlcHJvY2VzcyBQaGFzZQoKCiMgTWV0YSBkYXRhIAoKIFtGaXRiaXQgZGF0YSBkaWN0aW9uYXJ5IF0oaHR0cHM6Ly93d3cuZml0YWJhc2UuY29tL3Jlc291cmNlcy9rbm93bGVkZ2UtYmFzZS9leHBvcnRpbmctZGF0YS9kYXRhLWRpY3Rpb25hcmllcy8pCgoKIyBJbXBvcnQgbGlicmFyaWVzIApgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShza2ltcikKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGx1YnJpZGF0ZSkgIyB3b3JraW5nIHdpdGggZGF0ZXMKbGlicmFyeShSQ29sb3JCcmV3ZXIpICMgY29sb3IgcGFsZXR0ZQpsaWJyYXJ5KGdnY29ycnBsb3QpICMgVmlzdWFsaXphdGlvbiBvZiBhIGNvcnJlbGF0aW9uIG1hdHJpeCB1c2luZyBnZ3Bsb3QyCgoKIyBkaXNwbGF5LmJyZXdlci5hbGwoY29sb3JibGluZEZyaWVuZGx5ID0gVFJVRSkKYGBgCgoKIyBMb2FkIGRhdGFzZXRzIAoKYGBge3J9CiMgQ2xlYW4gZW52aXJvbm1lbnQKcm0obGlzdCA9IGxzKCkpCgpkYWlseV9hY3Rpdml0eSA8LQogIHJlYWRfY3N2KCJkYWlseUFjdGl2aXR5X21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKZGFpbHlfc2xlZXAgPC0gcmVhZF9jc3YoInNsZWVwRGF5X21lcmdlZC5jc3YiLAogIHRyaW1fd3MgPSBUUlVFLAogIHNob3dfY29sX3R5cGVzID0gRkFMU0UKKQoKaG91cmx5X2NhbG9yaWVzIDwtCiAgcmVhZF9jc3YoImhvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKaG91cmx5X2ludGVuc2l0aWVzIDwtCiAgcmVhZF9jc3YoImhvdXJseUludGVuc2l0aWVzX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQpob3VybHlfc3RlcHMgPC0KICByZWFkX2NzdigiaG91cmx5U3RlcHNfbWVyZ2VkLmNzdiIsCiAgICB0cmltX3dzID0gVFJVRSwKICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UKICApCgptaW51dGVfc2xlZXAgPC0KICByZWFkX2NzdigibWludXRlU2xlZXBfbWVyZ2VkLmNzdiIsCiAgICB0cmltX3dzID0gVFJVRSwKICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UKICApCgp3ZWlnaHRfbG9ncyA8LQogIHJlYWRfY3N2KCJ3ZWlnaHRMb2dJbmZvX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKc2Vjb25kc19oZWFydHJhdGUgPC0KICByZWFkX2NzdigiaGVhcnRyYXRlX3NlY29uZHNfbWVyZ2VkLmNzdiIsCiAgICB0cmltX3dzID0gVFJVRSwKICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UKICApCgojIFJlbW92ZSB0cmFpbGluZyBzcGFjZXMgKHRyaW1fd3MgPSBUUlVFKQpgYGAKCgoKIyBDbGVhbiBkYXRhIHNldHMKCiMjIENsZWFuIHRoZSBkYWlseV9hY3Rpdml0eSBkYXRhIHNldApgYGB7cn0KIyBDaGVjayBkYWlseV9hY3Rpdml0eSBkYXRhIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShkYWlseV9hY3Rpdml0eSkKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMKY2F0KAogICJcbiIsCiAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgc3VtKGlzLm5hKGRhaWx5X2FjdGl2aXR5KSksCiAgIlxuIiwKICAiRHVwbGljYXRlIHZhbHVlczoiLAogIHN1bShkdXBsaWNhdGVkKGRhaWx5X2FjdGl2aXR5KSksCiAgIlxuIiwKICAiVW5pcXVlIElkczoiLAogIG5fZGlzdGluY3QoZGFpbHlfYWN0aXZpdHkkSWQpCikKYGBgCgpMZXQgdXMgY2xlYW46Ci0gQ2hhbmdlIGNvbHVtbiBuYW1lcyB0byBsb3dlciBjYXNlIGJlY2F1c2UgUiBpcyBjYXNlIHNlbnNpdGl2ZQotIENoYW5nZSAiSWQiIGZyb20gZG91YmxlIHRvIGEgY2hhcmFjdGVyIGJlY2F1c2UgdGhlIG51bWJlciByZXByZXNlbnRzIGEgY2F0ZWdvcnkKLSBDaGFuZ2UgIkFjdGl2aXR5RGF0ZSIgZnJvbSBjaGFyIHRvIGRhdGUgCgpgYGB7cn0KIyBDbGVhbiBkYWlseV9hY3Rpdml0eSBkYXRhIHNldAoKZGFpbHlfYWN0aXZpdHkgPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKGRhaWx5X2FjdGl2aXR5KSAlPiUKICAjIENvcnJlY3QgY29sdW1uIHR5cGVzCiAgbXV0YXRlKGlkID0gYXMuY2hhcmFjdGVyKGlkKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKGFjdGl2aXR5X2RhdGUgPSBhcy5EYXRlKGFjdGl2aXR5X2RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlbS8lZC8lWSIpKSAlPiUgIyBmcm9tIGNociB0byBkYXRlCiAgIyBSZW1vdmUgZHVwbGljYXRlIHJvd3MKICBkaXN0aW5jdCgpCgojIENoZWNrIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0IGFmdGVyIGNsZWFuaW5nCmdsaW1wc2UoZGFpbHlfYWN0aXZpdHkpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzIGFmdGVyIGNsZWFuaW5nCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoZGFpbHlfYWN0aXZpdHkpKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQoZGFpbHlfYWN0aXZpdHkpKSkKYGBgCgoKCmBgYHtyfQojIExldCB1cyBwcmludCBzdW1tYXJ5IHN0YXRpc3RpYyB0byBoYXZlIGEgYmV0dGVyIGlkZWEgb2YgdGhlIGRhdGEgc2V0CmRhaWx5X2FjdGl2aXR5ICU+JQogIHN1bW1hcnkoKQpgYGAKClRoaXMgc3VtbWFyeSBoZWxwcyB1cyBleHBsb3JlIHF1aWNrbHkgZWFjaCBhdHRyaWJ1dGUuIFdlIG5vdGljZSB0aGF0IHNvbWUgYXR0cmlidXRlcyBoYXZlIG1pbmltdW0gdmFsdWUgb2YgemVybyAodG90YWxfc3RlcCwgdG90YWxfZGlzdGFuY2UsIGNhbG9yaWVzKS4gCkxldCB1cyBleHBsb3JlIHRoaXMgb2JzZXJ2YXRpb24uCgoKYGBge3J9CiMgQ2hlY2sgd2hlcmUgdG90YWxfc3RlcHMgaXMgemVybwpmaWx0ZXIoZGFpbHlfYWN0aXZpdHksIHRvdGFsX3N0ZXBzID09IDApCmBgYApXZSBmb3VuZCA3NyBvYnNlcnZhdGlvbnMgd2hlcmUgdG90YWxfc3RlcHMgaXMgemVyby4gV2Ugc2hvdWxkIGRlbGV0ZSB0aGVzZSBvYnNlcnZhdGlvbnMgc28gdGhhdCB0aGV5IGRvIG5vdCBhZmZlY3Qgb3VyIHRoZSBtZWFuIGFuZCBtZWRpYW4uCklmIHRvdGFsX3N0ZXAgaXMgemVybyB0aGF0IG1lYW5zIHRoYXQgdGhlIHBlcnNvbiBkaWQgbm90IHdlYXIgdGhlIEZpdGJpdC4KCmBgYHtyfQojIENoZWNrIHdoZXJlIGNhbG9yaWVzIGlzIHplcm8KZmlsdGVyKGRhaWx5X2FjdGl2aXR5LCBjYWxvcmllcyA9PSAwKQpgYGAKCmBgYHtyfQojIENoZWNrIHdoZXJlIHRvdGFsX2Rpc3RhbmNlIGlzIHplcm8KZmlsdGVyKGRhaWx5X2FjdGl2aXR5LCB0b3RhbF9kaXN0YW5jZSA9PSAwKQpgYGAKIEZyb20gb3VyIGluc3BlY3Rpb24gYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCB3ZSBqdXN0IG5lZWQgdG8gZGVsZXRlIHRoZSBlbnRyaWVzIHdoZXJlIHRvdGFsX3N0ZXBzIGlzIHplcm8gYW5kIHdpbGwgdGFrZSB0YWtlIGNhcmUgb2YgdGhlIHJlc3QuCgoKYGBge3J9CmRhaWx5X2FjdGl2aXR5X2NsZWFuIDwtCiAgZmlsdGVyKGRhaWx5X2FjdGl2aXR5LAogICAgICAgICB0b3RhbF9zdGVwcyAhPSAwLAogICAgICAgICB0b3RhbF9kaXN0YW5jZSAhPSAwLAogICAgICAgICBjYWxvcmllcyAhPSAwKQpkYWlseV9hY3Rpdml0eV9jbGVhbgoKYGBgCgoKCgpgYGB7cn0KbmFtZXMoZGFpbHlfYWN0aXZpdHkpCmBgYAoKCmBgYHtyfQojIENoZWNrIHRoZSBhdHRyaWJ1dGVzIGFnYWluCgpjYXQoIkJlZm9yZSBkZWxldGluZyB0aGUgZW50cmllc1xuXG4iKQpzZWxlY3QoZGFpbHlfYWN0aXZpdHksdG90YWxfc3RlcHMsdG90YWxfZGlzdGFuY2UsY2Fsb3JpZXMpICU+JQogIHN1bW1hcnkoKQoKY2F0KCJcblxuXG4iLAogICAgIlx0XHQgdnMiLAogICAgIlxuXG5cbiIpCgoKY2F0KCJBZnRlciBkZWxldGluZyB0aGUgZW50cmllc1xuXG4iKQpzZWxlY3QoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIHRvdGFsX3N0ZXBzLCB0b3RhbF9kaXN0YW5jZSwgY2Fsb3JpZXMpICU+JQogIHN1bW1hcnkoKQpgYGAKV2UgY2FuIHNlZSB0aGF0IHRoZSBvYnNlcnZhdGlvbiB3ZSByZW1vdmVkIGFmZmVjdGVkIG91ciBtZWFuIGFuZCBtZWRpYW4uCgoKCiMjIENsZWFuIHRoZSBkYWlseV9zbGVlcCBkYXRhIHNldApgYGB7cn0KIyBDaGVjayBkYWlseV9zbGVlcCBkYXRhIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShkYWlseV9zbGVlcCkKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYShkYWlseV9zbGVlcCkpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChkYWlseV9zbGVlcCkpLAogICJcbiIsCiAgIlVuaXF1ZSBJZHM6IiwKICBuX2Rpc3RpbmN0KGRhaWx5X3NsZWVwJElkKQogICAgKQpgYGAKCkxldCB1cyBjbGVhbjoKCi0gQ2hhbmdlIGNvbHVtbiBuYW1lcyB0byBsb3dlciBjYXNlIGJlY2F1c2UgUiBpcyBjYXNlIHNlbnNpdGl2ZQotIENoYW5nZSAiSWQiIGZyb20gZG91YmxlIHRvIGEgY2hhcmFjdGVyIGJlY2F1c2UgdGhlIG51bWJlciByZXByZXNlbnRzIGEgY2F0ZWdvcnkKLSBDaGFuZ2UgIlNsZWVwRGF5IiBmcm9tIGNoYXIgdG8gZGF0ZS4gU2luY2UgdGhlIHRpbWUgY29tcG9uZW50IG9mIHRoaXMgY29sdW1uIGlzIHRoZQogIHNhbWUgZm9yIGVhY2ggb2JzZXJ2YXRpb24iMTI6MDA6MDAgQU0iLCB3ZSBjYW4gcmVtb3ZlIGl0LiBUaGlzIHdpbGwgaGVscHMgdXMgbWVyZ2VkIHRoaXMgCiAgZGF0YSBzZXQgd2l0aCBkYWlseV9hY3Rpdml0eSBsYXRlcgotIERlbGV0ZSBkdXBsaWNhdGVzICgzIG9ic2VydmF0aW9ucyBhcmUgZHVwbGljYXRlcykKCgoKYGBge3J9CiMgQ2xlYW4gZGFpbHlfc2xlZXAgZGF0YSBzZXQKCmRhaWx5X3NsZWVwX2NsZWFuIDwtCiAgIyBDbGVhbiBjb2x1bW4gbmFtZXMKICBjbGVhbl9uYW1lcyhkYWlseV9zbGVlcCkgJT4lCiAgIyBDb3JyZWN0IGNvbHVtbiB0eXBlcwogIG11dGF0ZShpZCA9IGFzLmNoYXJhY3RlcihpZCkpICU+JSAjIGZyb20gZG91YmxlIHRvIGNocgogIG11dGF0ZShzbGVlcF9kYXkgPSBhcy5EYXRlKHNsZWVwX2RheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiJW0vJWQvJVkiKSkgJT4lICMgZnJvbSBjaHIgdG8gZGF0ZQogICMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzCiAgZGlzdGluY3QoKQoKIyBDaGVjayBjbGVhbiBkYWlseV9zbGVlcCBkYXRhIHNldApnbGltcHNlKGRhaWx5X3NsZWVwX2NsZWFuKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcyBhZnRlciBjbGVhbmluZwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGRhaWx5X3NsZWVwX2NsZWFuKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKGRhaWx5X3NsZWVwX2NsZWFuKSkpCmBgYAoKCgojIyBDbGVhbiB0aGUgaG91cmx5IGRhdGEgc2V0cyAoaG91cmx5X2NhbG9yaWVzLCBob3VybHlfaW50ZW5zaXRpZXMsIGFuZCBob3VybHlfc3RlcHMpCgoKYGBge3J9CiMgQ2hlY2sgaG91cmx5X2NhbG9yaWVzIGRhdGEgc2V0IGJlZm9yZSBjbGVhbmluZwpnbGltcHNlKGhvdXJseV9jYWxvcmllcykKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYShob3VybHlfY2Fsb3JpZXMpKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQoaG91cmx5X2NhbG9yaWVzKSkpCmBgYAoKYGBge3J9CiMgQ2hlY2sgaG91cmx5X2ludGVuc2l0aWVzIGRhdGEgc2V0IGJlZm9yZSBjbGVhbmluZwpnbGltcHNlKGhvdXJseV9pbnRlbnNpdGllcykKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYShob3VybHlfaW50ZW5zaXRpZXMpKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQoaG91cmx5X2ludGVuc2l0aWVzKSkpCmBgYAoKCgpgYGB7cn0KIyBDaGVjayBob3VybHlfc3RlcHMgZGF0YSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2UoaG91cmx5X3N0ZXBzKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGhvdXJseV9zdGVwcykpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChob3VybHlfc3RlcHMpKSkKYGBgCgojIyMgSm9pbiBob3VybHkgZGF0YSBzZXRzIHRvIGNyZWF0ZSBhIGhvdXJseV9hY3RpdHZpdHkgZGF0YSBzZXQKVGhlc2UgZGF0YSBzZXRzIHNoYXJlZCB0aGUgc2FtZSBJZCBhbmQgQWN0aXZpdHlfaG91ciwgbGV0IHVzIGpvaW4gdGhlbSBpbnRvIGEgbmV3IGRhdGEgc2V0IChob3VybHlfYWN0aXZpdHkpIGJlZm9yZSB3ZSBjbGVhbiB0aGVtLgoKCgpgYGB7cn0KIyBKb2luIHRoZSBob3VybHkgZGF0YSBzZXRzIChob3VybHlfY2Fsb3JpZXMsIGhvdXJseV9pbnRlbnNpdGllcywgYW5kIGhvdXJseV9zdGVwcykKCmhvdXJseV9hY3Rpdml0eSA8LQogIGlubmVyX2pvaW4oaG91cmx5X2NhbG9yaWVzLAogICAgICAgICAgICAgaG91cmx5X2ludGVuc2l0aWVzLAogICAgICAgICAgICAgYnkgPSBjKCJJZCIsICJBY3Rpdml0eUhvdXIiKSkKCmhvdXJseV9hY3Rpdml0eSA8LQogIGlubmVyX2pvaW4oaG91cmx5X2FjdGl2aXR5LCBob3VybHlfc3RlcHMsIGJ5ID0gYygiSWQiLCAiQWN0aXZpdHlIb3VyIikpCmBgYAoKCgpgYGB7cn0KIyBDaGVjayBob3VybHlfYWN0aXZpdHkgZGF0YSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2UoaG91cmx5X2FjdGl2aXR5KQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGhvdXJseV9hY3Rpdml0eSkpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChob3VybHlfYWN0aXZpdHkpKSkKYGBgCgpMZXQgdXMgY2xlYW46CgotIENoYW5nZSBjb2x1bW4gbmFtZXMgdG8gbG93ZXIgY2FzZSBiZWNhdXNlIFIgaXMgY2FzZSBzZW5zaXRpdmUKLSBDaGFuZ2UgIklkIiBmcm9tIGRvdWJsZSB0byBhIGNoYXJhY3RlciBiZWNhdXNlIHRoZSBudW1iZXIgcmVwcmVzZW50cyBhIGNhdGVnb3J5Ci0gQ2hhbmdlICJBY3Rpdml0eUhvdXIiIGZyb20gY2hhciB0byBkYXRldGltZQoKTm90ZTpUaGUgZGVmYXVsdCB0aW1lem9uZSBpcyBVVEMuCgoKCmBgYHtyfQojIENsZWFuIGhvdXJseV9hY3Rpdml0eSBkYXRhIHNldAoKaG91cmx5X2FjdGl2aXR5X2NsZWFuIDwtCiAgIyBDbGVhbiBjb2x1bW4gbmFtZXMKICBjbGVhbl9uYW1lcyhob3VybHlfYWN0aXZpdHkpICU+JQogICMgQ29ycmVjdCBjb2x1bW4gdHlwZXMKICBtdXRhdGUoaWQgPSBhcy5jaGFyYWN0ZXIoaWQpKSAlPiUgIyBmcm9tIGRvdWJsZSB0byBjaHIKICBtdXRhdGUoYWN0aXZpdHlfaG91ciA9IGFzX2RhdGV0aW1lKGFjdGl2aXR5X2hvdXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiKSkgJT4lICMgZnJvbSBjaHIgdG8gZGF0ZXRpbWUKICAjIFJlbW92ZSBkdXBsaWNhdGUgcm93cwogIGRpc3RpbmN0KCkKCiMgQ2hlY2sgY2xlYW4gZGFpbHlfYWN0aXZpdHkgZGF0YSBzZXQKZ2xpbXBzZShob3VybHlfYWN0aXZpdHlfY2xlYW4pCgoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcyBhZnRlciBjbGVhbmluZwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGhvdXJseV9hY3Rpdml0eV9jbGVhbikpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChob3VybHlfYWN0aXZpdHlfY2xlYW4pKSkKCiMgYXNfZGF0ZXRpbWUoKSBjb252ZXJ0cyB3aXRoIGRlZmF1bHQgdGltZXpvbmUgPSAiVVRDIgpgYGAKCgoKCgojIyBDbGVhbiB0aGUgbWludXRlX3NsZWVwIGRhdGEgc2V0CgpgYGB7cn0KIyBDaGVjayBtaW51dGVfc2xlZXAgZGF0YSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2UobWludXRlX3NsZWVwKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKG1pbnV0ZV9zbGVlcCkpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChtaW51dGVfc2xlZXApKSwKICAgICJcbiIsCiAgIlVuaXF1ZSBJZHM6IiwKICBuX2Rpc3RpbmN0KG1pbnV0ZV9zbGVlcCRJZCkpCgpgYGAKCkxldCB1cyBjbGVhbjoKCi0gQ2hhbmdlIGNvbHVtbiBuYW1lcyB0byBsb3dlciBjYXNlIGJlY2F1c2UgUiBpcyBjYXNlIHNlbnNpdGl2ZQotIENoYW5nZSAiSWQiIGZyb20gZG91YmxlIHRvIGEgY2hhcmFjdGVyIGJlY2F1c2UgdGhlIG51bWJlciByZXByZXNlbnRzIGEgY2F0ZWdvcnkKLSBDaGFuZ2UgImRhdGUiIGZyb20gY2hhciB0byBkYXRldGltZQotIENoYW5nZSAidmFsdWUiIGZyb20gZG91YmxlIHRvIGZhY3Rvci4gVmFsdWUgaW5kaWNhdGVzIHRoZSBzbGVlcCBzdGF0ZTogMSA9IGFzbGVlcCwgMiA9IHJlc3RsZXNzLCAzID0gYXdha2UuIFNlZTogCiBbRml0Yml0IGRhdGEgZGljdGlvbmFyeSBdKGh0dHBzOi8vd3d3LmZpdGFiYXNlLmNvbS9yZXNvdXJjZXMva25vd2xlZGdlLWJhc2UvZXhwb3J0aW5nLWRhdGEvZGF0YS1kaWN0aW9uYXJpZXMvKQogLSBSZW1vdmUgZHVwbGljYXRlIHZhbHVlczogNTQzCgoKYGBge3J9CiMgQ2xlYW4gbWludXRlX3NsZWVwIGRhdGEgc2V0CgptaW51dGVfc2xlZXBfY2xlYW4gPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKG1pbnV0ZV9zbGVlcCkgJT4lCiAgIyBDb3JyZWN0IGNvbHVtbiB0eXBlcwogIG11dGF0ZSh2YWx1ZSA9IGFzLmZhY3Rvcih2YWx1ZSkpICU+JSAjIGZyb20gZG91YmxlIHRvIGNocgogIG11dGF0ZShpZCA9IGFzLmNoYXJhY3RlcihpZCkpICU+JSAjIGZyb20gZG91YmxlIHRvIGNocgogIG11dGF0ZShkYXRlID0gYXNfZGF0ZXRpbWUoZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIpKSAlPiUgIyBGcm9tIGNociB0byBkYXRldGltZQogICMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzCiAgZGlzdGluY3QoKQoKIyBDaGVjayBjbGVhbiBkYWlseV9hY3Rpdml0eSBkYXRhIHNldApnbGltcHNlKG1pbnV0ZV9zbGVlcF9jbGVhbikKCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzIGFmdGVyIGNsZWFuaW5nCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEobWludXRlX3NsZWVwX2NsZWFuKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKG1pbnV0ZV9zbGVlcF9jbGVhbikpKQpgYGAKCgojIyBDbGVhbiB0aGUgc2Vjb25kc19oZWFydHJhdGUgZGF0YSBzZXQKCgpgYGB7cn0KIyBDaGVjayBzZWNvbmRzX2hlYXJ0cmF0ZSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2Uoc2Vjb25kc19oZWFydHJhdGUpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgKICAiXG4iLAogICJNaXNzaW5nIHZhbHVlczoiLCBzdW0oaXMubmEoc2Vjb25kc19oZWFydHJhdGUpKSwKICAiXG4iLAogICJEdXBsaWNhdGUgdmFsdWVzOiIsIHN1bShkdXBsaWNhdGVkKHNlY29uZHNfaGVhcnRyYXRlKSkKKQpgYGAKCkxldCB1cyBjbGVhbjoKCi0gQ2hhbmdlIGNvbHVtbiBuYW1lcyB0byBsb3dlciBjYXNlIGJlY2F1c2UgUiBpcyBjYXNlIHNlbnNpdGl2ZQotIENoYW5nZSAiSWQiIGZyb20gZG91YmxlIHRvIGEgY2hhcmFjdGVyIGJlY2F1c2UgdGhlIG51bWJlciByZXByZXNlbnRzIGEgY2F0ZWdvcnkKLSBDaGFuZ2UgIlRpbWUiIGZyb20gY2hhciB0byBkYXRldGltZSBhbmQgcmVuYW1lIGl0IGRhdGVfdGltZQotIFJlbmFtZSAiVmFsdWUiIHRvIGhlYXJ0X3JhdGUKIFtGaXRiaXQgZGF0YSBkaWN0aW9uYXJ5IF0oaHR0cHM6Ly93d3cuZml0YWJhc2UuY29tL3Jlc291cmNlcy9rbm93bGVkZ2UtYmFzZS9leHBvcnRpbmctZGF0YS9kYXRhLWRpY3Rpb25hcmllcy8pCgoKYGBge3J9CiMgQ2xlYW4gc2Vjb25kc19oZWFydHJhdGUgZGF0YSBzZXQKCnNlY29uZHNfaGVhcnRyYXRlX2NsZWFuIDwtCiAgIyBDbGVhbiBjb2x1bW4gbmFtZXMKICBjbGVhbl9uYW1lcyhzZWNvbmRzX2hlYXJ0cmF0ZSkgJT4lCiAgIyBDb3JyZWN0IGNvbHVtbiB0eXBlcwogIG11dGF0ZShpZCA9IGFzLmNoYXJhY3RlcihpZCkpICU+JSAjIGZyb20gZG91YmxlIHRvIGNocgogIG11dGF0ZSh0aW1lID0gYXNfZGF0ZXRpbWUodGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIpKSAlPiUgIyBmcm9tIGNociB0byBkYXRldGltZQogICMgUmVuYW1lIGNvbHVtbnMKICByZW5hbWUoZGF0ZV90aW1lID0gdGltZSwKICAgICAgICAgaGVhcnRfcmF0ZSA9IHZhbHVlKSAlPiUKICAjIFJlbW92ZSBkdXBsaWNhdGUgcm93cwogIGRpc3RpbmN0KCkKCiMgQ2hlY2sgY2xlYW4gZGFpbHlfYWN0aXZpdHkgZGF0YSBzZXQKZ2xpbXBzZShzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbikKCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzIGFmdGVyIGNsZWFuaW5nCgpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKHNlY29uZHNfaGVhcnRyYXRlX2NsZWFuKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKHNlY29uZHNfaGVhcnRyYXRlX2NsZWFuKSkpCgoKIyBhc19kYXRldGltZSgpIGNvbnZlcnRzIHdpdGggZGVmYXVsdCB0aW1lem9uZSA9ICJVVEMiCmBgYAoKCgojIyBDbGVhbiB0aGUgd2VpZ2h0X2xvZ3MgZGF0YSBzZXQKCgpgYGB7cn0KIyBDaGVjayB3ZWlnaHRfbG9ncyBzZXQgYmVmb3JlIGNsZWFuaW5nCgpnbGltcHNlKHdlaWdodF9sb2dzKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKHdlaWdodF9sb2dzKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKHdlaWdodF9sb2dzKSkpCmBgYAoKCkxldCB1cyBjbGVhbjoKLSBDaGFuZ2UgY29sdW1uIG5hbWVzIHRvIGxvd2VyIGNhc2UgYmVjYXVzZSBSIGlzIGNhc2Ugc2Vuc2l0aXZlCi0gQ2hhbmdlICJJZCIgZnJvbSBkb3VibGUgdG8gYSBjaGFyYWN0ZXIgYmVjYXVzZSB0aGUgbnVtYmVyIHJlcHJlc2VudHMgYSBjYXRlZ29yeQotIENoYW5nZSAiRGF0ZSIgZnJvbSBjaGFyIHRvIGRhdGV0aW1lIGFuZCByZW5hbWUgaXQgZGF0ZV90aW1lCi0gQ2hhbmdlIE5BIHRvIDAgaW4gdGhlIGNvbHVtbiAiZmF0IgoKCmBgYHtyfQojIENsZWFuICB3ZWlnaHRfbG9ncyBkYXRhIHNldAoKd2VpZ2h0X2xvZ3NfY2xlYW4gPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKHdlaWdodF9sb2dzKSAlPiUKICAjIENvcnJlY3QgY29sdW1uIHR5cGVzCiAgbXV0YXRlKGlkID0gYXMuY2hhcmFjdGVyKGlkKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKGRhdGUgPSBhc19kYXRldGltZShkYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIikpICU+JSAjIGZyb20gY2hyIHRvIGRhdGV0aW1lCiAgIyBSZW5hbWUgY29sdW1ucwogIHJlbmFtZShkYXRlX3RpbWUgPSBkYXRlKSAlPiUKICAjIFJlbW92ZSBkdXBsaWNhdGUgcm93cwogIGRpc3RpbmN0KCkKCgoKIyBDaGFuZ2UgTkEgdG8gMCBpbiB0aGUgY29sdW1uICJmYXQiCndlaWdodF9sb2dzX2NsZWFuJGZhdFtpcy5uYSh3ZWlnaHRfbG9ncyRmYXQpXSA8LSAwCgojIENoZWNrIGNsZWFuIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0CmdsaW1wc2Uod2VpZ2h0X2xvZ3NfY2xlYW4pCgoKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMgYWZ0ZXIgY2xlYW5pbmcKCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEod2VpZ2h0X2xvZ3NfY2xlYW4pKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQod2VpZ2h0X2xvZ3NfY2xlYW4pKSkKCmBgYAoKCiMgRXhwb3J0IGNsZWFuIGRhdGEgc2V0cwoKYGBge3J9CiMgVG8gdW5jb21tZW50IHRoZSBmb2xsb3dpbmcgY29kZSwgc2VsZWN0IGFsbCB0aGUgbGluZXMgYW5kIHByZXNzIHNoaWZ0ICsgY29udHJvbCArIGMgb24gTWFjCgoKIyB3cml0ZS5jc3YoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIAojICAgICAgICAgICAiZGFpbHlfYWN0aXZpdHlfY2xlYW4uY3N2IiwgCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHdyaXRlLmNzdihkYWlseV9zbGVlcF9jbGVhbiwgCiMgICAgICAgICAgICJkYWlseV9zbGVlcF9jbGVhbi5jc3YiLCAKIyAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgd3JpdGUuY3N2KGRhaWx5X3NsZWVwX2NsZWFuLCAKIyAgICAgICAgICAgImhvdXJseV9hY3Rpdml0eV9jbGVhbi5jc3YiLCAKIyAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgd3JpdGUuY3N2KG1pbnV0ZV9zbGVlcF9jbGVhbiwgCiMgICAgICAgICAgICJtaW51dGVfc2xlZXBfY2xlYW4uY3N2IiwKIyAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgd3JpdGUuY3N2KHNlY29uZHNfaGVhcnRyYXRlX2NsZWFuLAojICAgICAgICAgICAic2Vjb25kc19oZWFydHJhdGVfY2xlYW4uY3N2IiwKIyAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgd3JpdGUuY3N2KHdlaWdodF9sb2dzX2NsZWFuICwgCiMgICAgICAgICAgICJ3ZWlnaHRfbG9nc19jbGVhbiAuY3N2IiwKIyAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCgpgYGAKCiMgQW5hbHl6ZSBQaGFzZSAKCgoKCgoKIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCgoKCiMjICBFREEgZm9yIGRhaWx5X2FjdGl2aXR5X2NsZWFuCmBgYHtyfQpzdHIoZGFpbHlfYWN0aXZpdHlfY2xlYW4pCmBgYAojIyMgVW5pdmFyaWF0ZSBhbmFseXNpcyBmb3IgZGFpbHlfYWN0aXZpdHlfY2xlYW4KCgojIyMgTnVtZXJpY2FsIHZhcmlhYmxlcwpgYGB7cn0KIyBTdWJzZXQgbnVtZXJpYyBjb2x1bW5zIApudW1fZGYgPC0gc2VsZWN0X2lmKGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBpcy5udW1lcmljKQoKIyBJZGVudGlmeSBudW1lcmljIGNvbHVtbnMKY29sbmFtZXMobnVtX2RmKQoKCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQoKIyBwbG90dGluZyBhbGwgbnVtZXJpY2FsIHZhcmlhYmxlcwpjb2xfbmFtZXMgPC0gY29sbmFtZXMobnVtX2RmKQpmb3IgKGkgaW4gY29sX25hbWVzKSB7CiAgc3VwcHJlc3NXYXJuaW5ncyhwcmludCgKICAgIGdncGxvdChudW1fZGYsIGFlcyhudW1fZGZbW2ldXSkpICsKICAgICAgZ2VvbV9oaXN0b2dyYW0oCiAgICAgICAgYmlucyA9IDMwLAogICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICBmaWxsID0gImdyYXkiLAogICAgICAgIGFlcyh5ID0gLi5kZW5zaXR5Li4pCiAgICAgICkgKwogICAgICBnZW9tX2RlbnNpdHkoCiAgICAgICAgY29sb3IgPSAiYmx1ZSIsCiAgICAgICAgc2l6ZSA9IDEKICAgICAgKSArCiAgICAgIHhsYWIoaSkgKyB5bGFiKCJDb3VudCIpICsKICAgICAgZ2d0aXRsZShwYXN0ZSgiSGlzdG9ncmFtIGFuZCBEZW5zaXR5IFBsb3Qgb2YiLCBpKSkKICApKQp9CgoKCgpgYGAKCgoKCk9ic2VydmF0aW9uczoKCi0gTWFueSB2YXJpYWJsZXMgc2hvdyBhIHJpZ2h0LXNrZXdlZCBkaXN0cmlidXRpb246IGEgbGFyZ2VyIG51bWJlciBvZiBkYXRhIHZhbHVlcyBhcmUgbG9jYXRlZCBvbiB0aGUgbGVmdCBzaWRlIG9mIHRoZSBjdXJ2ZQoKLSBUaGUgdmFyaWFibGVzIHRvdGFsX3N0ZXBzLCB0b3RhbF9kaXN0YW5jZSwgdHJhY2tlcl9kaXN0YW5jZSBoYXZlIGEgc2ltaWxhciBkaXN0cmlidXRpb24uIFdlIGNhbiBleHBsb3JlIHRoZWlyIGNvcnJlbGF0aW9ucyBsYXRlcgoKLSBTaW5jZSB0aGUgZGlzdHJpYnV0aW9ucyBhcmUgbm90IG5vcm1hbC4gVGhlIG1lZGlhbiBpcyBhIGJldHRlciBpbmRpY2F0b3Igb2YgY2VudHJhbCB0ZW5kZW5jeSBmb3IgdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gdGhlc2UgZGF0YSBzZXQKCi0gKipUaGUgdmFyaWFibGUgbG9nZ2VkX2FjdGl2aXRpZXNfZGlzdGFuY2UgYW5kIHNlZGVudGFyeV9hY3RpdmVfZGlzdGFuY2UgbWlnaHQgbm90IHByb3ZpZGUgdXNlZnVsIGluZm9ybWF0aW9uIHNpbmNlIG1vc3Qgb2YgdGhlIGRhdGEgcG9pbnRzIGFyZSB6ZXJvLiBJdCBzZWVtcyB0aGF0IHRoZSB1c2VycyBhcmUgbm90IGxvZ2dpbmcgdGhlIGRpc3RhbmNlIGZyZXF1ZW50bHkqKgoKLSAgVGhlIGZvbGxvd2luZyB2YXJpYWJsZXMgc2VlbSByZWxhdGVkLiBXZSB3aWxsIGV4cGxvcmUgdGhlbSBmdXJ0aGVyIGluIHRoZSBiaXZhcmlhdGUgYW5hbHlzaXMgc2VjdGlvbjoKCnNlZGVudGFyeV9taW51dGVzOyBzZWRlbnRhcnlfYWN0aXZlX2Rpc3RhbmNlIApsaWdodGx5X2FjdGl2ZV9taW51dGVzOyBsaWdodF9hY3RpdmVfZGlzdGFuY2UgIApmYWlybHlfYWN0aXZlX21pbnV0ZXM7IG1vZGVyYXRlbHlfYWN0aXZlX2Rpc3RhbmNlCnZlcnlfYWN0aXZlX21pbnV0ZXM7IHZlcnlfYWN0aXZlX2Rpc3RhbmNlICAgCgotIFRoZSB2YXJpYWJsZXMgY2Fsb3JpZXMgYW5kIHNlZGVudGFyeV9taW51dGVzIGV4aGliaXQgYSBtdWx0aW1vZGFsIGRpc3RyaWJ1dGlvbiwgaW5kaWNhdGluZyB0aGUgcHJlc2VuY2Ugb2Ygc3VicG9wdWxhdGlvbnMgd2l0aGluIHRoZSBkYXRhLiBJbiB0aGlzIGRhdGFzZXQsIGdlbmRlciBjb3VsZCBiZSBhIHBvdGVudGlhbCB2YXJpYWJsZSB0aGF0IHdvdWxkIHJlc3VsdCBpbiBhIGJpbW9kYWwgZGlzdHJpYnV0aW9uIHdoZW4gZXhhbWluaW5nIGhpc3RvZ3JhbXMgb2YgY2Fsb3JpZXMgYW5kIHNlZGVudGFyeSBtaW51dGVzLiBVbmZvcnR1bmF0ZWx5LCB0aGUgZ2VuZGVyIG9mIHRoZSB1c2VycyBpcyBub3QgcHJvdmlkZWQsIGxpbWl0aW5nIG91ciBhYmlsaXR5IHRvIGNvbmZpcm0gdGhpcyBoeXBvdGhlc2lzLgoKCgoKIyMjIENhdGVnb3JpY2FsIHZhcmlhYmxlcwoKYGBge3J9CiMgU3Vic2V0IG51bWVyaWMgY29sdW1ucyAKCnNlbGVjdF9pZihkYWlseV9hY3Rpdml0eV9jbGVhbiwgbmVnYXRlKGlzLm51bWVyaWMpKQoKYGBgCgoKYGBge3J9CiMgQ2hlY2sgY291bnRzIGJ5IGlkCmdncGxvdChkYXRhPWRhaWx5X2FjdGl2aXR5X2NsZWFuKSArIAogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMgKHg9IHJlb3JkZXIoaWQsIGlkLGxlbmd0aCkpKSsKICB4bGFiKCJpZCIpICsKICBjb29yZF9mbGlwKCkKICAKI2h0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS85MjMxODU3LzE1MzMzNTgwCgojcmVvcmRlcihpZCwgaWQsIGxlbmd0aCkgdGFrZXMgdGhlIGlkIHZhcmlhYmxlLCB1c2VzIGl0c2VsZiB0byBkZXRlcm1pbmUgdGhlIG9yZGVyLCBhbmQgdXNlcyB0aGUgbGVuZ3RoKCkgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSB2YWx1ZXMgdXNlZCBmb3Igb3JkZXJpbmcuIEVzc2VudGlhbGx5LCB0aGlzIHJlb3JkZXJzIHRoZSBsZXZlbHMgb2YgdGhlIGlkIHZhcmlhYmxlIGJhc2VkIG9uIHRoZSBsZW5ndGggb2YgdGhlaXIgbmFtZXMuCmBgYAoKCmBgYHtyfQpjb3VudF9tYXhfcmF0aW8gPC0gZGFpbHlfYWN0aXZpdHlfY2xlYW4gJT4lCiAgY291bnQoaWQpICU+JQogIHJlbmFtZShpZCA9ICJpZCIsIGNvdW50ID0gIm4iKSAlPiUKICBtdXRhdGUocGVyY2VudF9vZl9tYXggPSBjb3VudCAvIG1heChjb3VudCkgKiAxMDApICU+JQogIGFycmFuZ2UoZGVzYyhwZXJjZW50X29mX21heCkpCgoKYGBgCgoKCmBgYHtyfQoKIyBDcmVhdGUgYmFyIGdyYXBoIHdpdGggcGVyY2VudGFnZSBvZiBlbnRyaWVzIGNvbXBhcmVkIHRvIG1heGltdW0KZ2dwbG90KGNvdW50X21heF9yYXRpbywgYWVzKHggPSByZW9yZGVyKGlkLCBwZXJjZW50X29mX21heCksIHkgPSBwZXJjZW50X29mX21heCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHhsYWIoIklEIikgKwogIHlsYWIoIlBlcmNlbnRhZ2Ugb2YgTWF4aW11bSBDb3VudCIpICsKICBnZ3RpdGxlKCJDb3VudCBieSBJRCBhbmQgUGVyY2VudGFnZSBvZiBNYXhpbXVtIENvdW50IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTUwLCBjb2xvcj0ib3JhbmdlIiwgbGluZXdpZHRoPTEpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD03NSwgY29sb3I9InJlZCIsIGxpbmV3aWR0aD0xKSsKICBjb29yZF9mbGlwKCkKCgpgYGAKCmBgYHtyfQojIHBlcmNlbnRfb2ZfbWF4ID4gNzUlCgpwZXJjZW50X29mX21heF90b3BfNzUgPC0gZmlsdGVyKGNvdW50X21heF9yYXRpbywgcGVyY2VudF9vZl9tYXggPj03NSkKcGVyY2VudF9vZl9tYXhfdG9wXzc1IApgYGAKCmBgYHtyfQojIHBlcmNlbnRfb2ZfbWF4IDwgNzUKCnBlcmNlbnRfb2ZfbWF4X3VuZGVyXzc1IDwtIGZpbHRlcihjb3VudF9tYXhfcmF0aW8sIHBlcmNlbnRfb2ZfbWF4IDwgNzUpCnBlcmNlbnRfb2ZfbWF4X3VuZGVyXzc1IApgYGAKCgoKYGBge3J9CmRhaWx5X2FjdGl2aXR5X2NsZWFuJGFjdGl2aXR5X2RhdGUgJT4lIHN1bW1hcnkoKQpgYGAKCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGFpbHlfYWN0aXZpdHlfY2xlYW4gLCBhZXMoeCA9IGFjdGl2aXR5X2RhdGUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHggPSAiQWN0aXZpdHkgRGF0ZSIsIHkgPSAiRnJlcXVlbmN5IiwgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEFjdGl2aXR5IERhdGUiKSAKCmBgYAoKCgpPYnNlcnZhdGlvbnM6CgotIEl0IGFwcGVhcnMgdGhhdCB0aGVyZSBpcyBtaXNzaW5nIGFjdGl2aXR5IGRhdGEgdG93YXJkcyB0aGUgZW5kIG9mIHRoZSBhdmFpbGFibGUgcGVyaW9kLCBzcGVjaWZpY2FsbHkgaW4gdGhlIGJlZ2lubmluZyBvZiBNYXkKCgoKYGBge3J9CiMgSW52ZXN0aWdhdGUgaWYgdGhlIG1pc3NpbmcgYWN0aXZpdHkgZGF0YSBjb2luY2lkZXMgd2l0aCB0aGUgYWJzZW5jZSBvZiBlbnRyaWVzIGZvciBjZXJ0YWluIHVzZXIgSURzLgoKZ2dwbG90KGRhdGE9c3Vic2V0KGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBpZCAlaW4lIHBlcmNlbnRfb2ZfbWF4X3RvcF83NSRpZCksIGFlcyh4ID0gYWN0aXZpdHlfZGF0ZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAibGlnaHRibHVlIikgKwogIGxhYnMoeCA9ICJBY3Rpdml0eSBEYXRlIiwgeSA9ICJGcmVxdWVuY3kiLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQWN0aXZpdHkgRGF0ZSBGb3IgSURzIHdpdGggQWJvdmUgNzUlIG9mIEVudHJpZXMiKQpgYGAKYGBge3J9CgpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGlkICVpbiUgcGVyY2VudF9vZl9tYXhfdW5kZXJfNzUkaWQpLCBhZXMoeCA9IGFjdGl2aXR5X2RhdGUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHggPSAiQWN0aXZpdHkgRGF0ZSIsIHkgPSAiRnJlcXVlbmN5IiwgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEFjdGl2aXR5IERhdGUgRm9yIElEcyB3aXRoIHVuZGVyIDc1JSBvZiBFbnRyaWVzIikKYGBgCi0gVXNlcnMgd2l0aCBtb3JlIHRoYW4gNzUlIG9mIGRhdGEgY29uc2lzdGVudGx5IHJlcG9ydCBhY3Rpdml0eSBkYXRlcywgd2hpbGUgdGhvc2Ugd2l0aCBsZXNzIHRoYW4gNzUlIG9mIGRhdGEgc2hvdyBhIGRlY2xpbmUgaW4gcmVwb3J0aW5nIHN0YXJ0aW5nIGZyb20gdGhlIGVuZCBvZiBBcHJpbC4gVGhlIGRlY2xpbmUgaW4gQWN0aXZpdHkgRGF0ZSBzZWVtcyB0byBiZSBwcmltYXJpbHkgZHVlIHRvIGEgbGFjayBvZiBkYXRhIHJlcG9ydGluZyBmcm9tIHNvbWUgdXNlcnMgZHVyaW5nIHRoYXQgcGVyaW9kLgoKCgojIyMgQml2YXJpYXRlIGFuYWx5c2lzCgoKIyMjIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIG51bWVyaWNhbCB2YXJpYWJsZXMKCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpjb3JyIDwtIGNvcihzZWxlY3RfaWYoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGlzLm51bWVyaWMpKQoKZ2djb3JycGxvdChjb3JyLAogICAgICAgICAgIGhjLm9yZGVyID0gVFJVRSwKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICBsYWIgPSBUUlVFLAogICAgICAgICAgIGNvbG9ycyA9IGMoImZpcmVicmljayIsICJ3aGl0ZSIsICJyb3lhbGJsdWUiKSwKICAgICAgICAgICBsYWJfc2l6ZSA9IDQsCiAgICAgICAgICAgbGFiX2NvbCA9ICJibGFjayIsCiAgICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYXRpb24gQmV0d2VlbiBOdW1lcmljYWwgVmFyaWFibGVzIikKCiNodHRwczovL3JkcnIuaW8vZ2l0aHViL21pY3JvcmVzZWFyY2hlci9NaWNyb1Zpcy9tYW4vZ2djb3JycGxvdC5odG1sCmBgYApzZWRlbnRhcnlfbWludXRlczsgc2VkZW50YXJ5X2FjdGl2ZV9kaXN0YW5jZSAKbGlnaHRseV9hY3RpdmVfbWludXRlczsgbGlnaHRfYWN0aXZlX2Rpc3RhbmNlICAKZmFpcmx5X2FjdGl2ZV9taW51dGVzOyBtb2RlcmF0ZWx5X2FjdGl2ZV9kaXN0YW5jZQp2ZXJ5X2FjdGl2ZV9taW51dGVzOyB2ZXJ5X2FjdGl2ZV9kaXN0YW5jZSAgIAoKCgoKYGBge3J9CiMgQ29tcHV0ZSBjb3JyZWxhdGlvbiBtYXRyaXgKY29ycl9tYXRyaXggPC0gY29ycgoKIyBTZXQgdGhlIHRocmVzaG9sZCBmb3IgY29ycmVsYXRpb24KdGhyZXNob2xkIDwtIDAuNjAKCiMgRmluZCBwYWlycyBvZiBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMKaGlnaF9jb3JfcGFpcnMgPC0gd2hpY2goYWJzKGNvcnJfbWF0cml4KSA+IHRocmVzaG9sZCAmIGxvd2VyLnRyaShjb3JyX21hdHJpeCwgZGlhZyA9IEZBTFNFKSwgYXJyLmluZCA9IFRSVUUpCgojIEV4dHJhY3QgdGhlIHZhcmlhYmxlIG5hbWVzIGFuZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgZm9yIHRoZSBjb3JyZWxhdGVkIHBhaXJzCnZhcmlhYmxlX25hbWVzIDwtIGNvbG5hbWVzKGNvcnJfbWF0cml4KQpjb3JfdmFsdWVzIDwtIGFzLnZlY3Rvcihjb3JyX21hdHJpeFtoaWdoX2Nvcl9wYWlyc10pCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIGNvcnJlbGF0ZWQgcGFpcnMgYW5kIHRoZWlyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwpjb3JfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlMSA9IHZhcmlhYmxlX25hbWVzW2hpZ2hfY29yX3BhaXJzWywgMV1dLAogIFZhcmlhYmxlMiA9IHZhcmlhYmxlX25hbWVzW2hpZ2hfY29yX3BhaXJzWywgMl1dLAogIENvcnJlbGF0aW9uID0gY29yX3ZhbHVlcwopCgojIFNvcnQgdGhlIGNvcnJlbGF0ZWQgcGFpcnMgYnkgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaW4gZGVzY2VuZGluZyBvcmRlcgpzb3J0ZWRfY29yX2RhdGEgPC0gY29yX2RhdGFbb3JkZXIoLWNvcl9kYXRhJENvcnJlbGF0aW9uKSwgXQoKIyBSZW1vdmUgdGhlIGluZGV4CnJvdy5uYW1lcyhzb3J0ZWRfY29yX2RhdGEpIDwtIE5VTEwKCiMgRGlzcGxheSB0aGUgc29ydGVkIGNvcnJlbGF0ZWQgdmFyaWFibGUgcGFpcnMgaW4gdGhlIGRhdGFmcmFtZQpwcmludChzb3J0ZWRfY29yX2RhdGEpCgpgYGAKCgotIFRvdGFsX2Rpc3RhbmNlLCB0cmFja2VyX2Rpc3RhbmNlLCBhbmQgdG90YWwgc3RlcHMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCBzbyB3ZSB3aWxsIHJldGFpbiBvbmx5IHRvdGFsIGRpc3RhbmNlIGFuZCB0b3RhbCBzdGVwcyBhcyB0aGV5IHByb3ZpZGUgc2ltaWxhciBpbmZvcm1hdGlvbi4KCi0gVGhlIGZvbGxvd2luZyBtaW51dGUgYW5kIGRpc3RhbmNlIHR5cGVzIGFyZSBjb3JyZWxhdGVkLiBXaGljaCBpbmRpY2F0ZXMgdGhhdCB0aGV5IHJlcG9ydCBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgc2FtZSBhY3Rpdml0eSwgdGhpcyBpcyB0aW1lIG9yIGRpc3RhbmNlOgogIC0gbGlnaHRseV9hY3RpdmVfbWludXRlcyBhbmQgbGlnaHRfYWN0aXZlX2Rpc3RhbmNlIChjb3JyID0gMC44NSkKICAtIGZhaXJseV9hY3RpdmVfbWludXRlcyBhbmQgbW9kZXJhdGVseV9hY3RpdmVfZGlzdGFuY2UgKGNvcnIgPSAwLjk0KQogIC0gdmVyeV9hY3RpdmVfbWludXRlcwlhbmQgdmVyeV9hY3RpdmVfZGlzdGFuY2UgKGNvcnIgPSAwLjgyKQoKLSBUaGVyZSBpcyBhIG1vZGVyYXRlbHkgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0aW1lIHNwZW50IGR1cmluZyB2ZXJ5IGFjdGl2ZSBwZXJpb2RzIGFuZCB0aGUgdG90YWwgbnVtYmVyIG9mIHN0ZXBzL3RvdGFsIGRpc3RhbmNlOgogIC0gVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmVyeV9hY3RpdmVfbWludXRlcyBhbmQgdG90YWxfZGlzdGFuY2UgaXMgMC42OAogIC0gVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmVyeV9hY3RpdmVfbWludXRlcyBhbmQgdG90YWxfc3RlcHMgaXMgMC42NgoKLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIG9mIDAuNjEgYmV0d2VlbiB0aGUgdG90YWwgZHVyYXRpb24gb2YgdmVyeSBhY3RpdmUgbWludXRlcyBhbmQgdGhlIGVzdGltYXRlZCBkYWlseSBjYWxvcmllcyBjb25zdW1lZC4KLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIG9mIDAuNjIgYmV0d2VlbiB0aGUgdG90YWwgZGlzdGFuY2UgY292ZXJlZCBhbmQgdGhlIGVzdGltYXRlZCBkYWlseSBjYWxvcmllcyBjb25zdW1lZC4KLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG9mIDAuNjAgYmV0d2VlbiB0aGUgZGlzdGFuY2UgY292ZXJlZCBkdXJpbmcgbGlnaHQgYWN0aXZpdHkgKGxpZ2h0X2FjdGl2ZV9kaXN0YW5jZSkgYW5kIHRoZSB0b3RhbCBudW1iZXIgb2Ygc3RlcHMgdGFrZW4gKHRvdGFsX3N0ZXBzKS4KCgojIyMjIFNjYXR0ZXJwbG90cyBvZiBzZWxlY3RlZCBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMgcGFpcnMgKD4wLjYwKQoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CgojIExpc3Qgb2YgY29ycmVsYXRlZCB2YXJpYWJsZSBwYWlycwpjb3JyZWxhdGVkX3BhaXJzIDwtIGxpc3QoYygidG90YWxfc3RlcHMiLCAidG90YWxfZGlzdGFuY2UiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJsaWdodGx5X2FjdGl2ZV9taW51dGVzIiwgImxpZ2h0X2FjdGl2ZV9kaXN0YW5jZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgYygiZmFpcmx5X2FjdGl2ZV9taW51dGVzIiwgIm1vZGVyYXRlbHlfYWN0aXZlX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInZlcnlfYWN0aXZlX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInRvdGFsX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInRvdGFsX3N0ZXBzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgImNhbG9yaWVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ0b3RhbF9kaXN0YW5jZSIsICJjYWxvcmllcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgYygibGlnaHRfYWN0aXZlX2Rpc3RhbmNlIiwgInRvdGFsX3N0ZXBzIikpCgojIExvb3Agb3ZlciBlYWNoIHBhaXIgYW5kIGNyZWF0ZSBzY2F0dGVyIHBsb3QKZm9yIChwYWlyIGluIGNvcnJlbGF0ZWRfcGFpcnMpIHsKICB2YXIxIDwtIHBhaXJbMV0KICB2YXIyIDwtIHBhaXJbMl0KICAKICAjIENhbGN1bGF0ZSBhdmVyYWdlcwogIGF2Z192YXIxIDwtIG1lYW4oZGFpbHlfYWN0aXZpdHlfY2xlYW5bW3ZhcjFdXSwgbmEucm0gPSBUUlVFKQogIGF2Z192YXIyIDwtIG1lYW4oZGFpbHlfYWN0aXZpdHlfY2xlYW5bW3ZhcjJdXSwgbmEucm0gPSBUUlVFKQogIAogICMgQ3JlYXRlIHNjYXR0ZXIgcGxvdCB1c2luZyBnZ3Bsb3QyIHdpdGggYWVzKCkKICBwcmludChnZ3Bsb3QoZGF0YSA9IGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBhZXMoeCA9ICEhc3ltKHZhcjEpLCB5ID0gISFzeW0odmFyMikpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYXZnX3ZhcjEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGF2Z192YXIyLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKwogICAgeGxhYih2YXIxKSArIHlsYWIodmFyMikgKwogICAgZ2d0aXRsZShwYXN0ZSgiU2NhdHRlciBQbG90IHdpdGggQXZlcmFnZSBSZWZlcmVuY2UgTGluZXMgb2YiLCB2YXIxLCAidnMiLCB2YXIyKSkpCn0KYGBgCgoKIyMjIFVzZXIgQmVoYXZpb3IgZm9yIGRhaWx5IGFjdGl2aXR5IGRhdGFzZXQKCiMjIyMgVG90YWwgc3RlcHM6IFRvdGFsIG51bWJlciBvZiBzdGVwcyB0YWtlbi4KCmBgYHtyfQojIENyZWF0ZSBhIGJveHBsb3QgZm9yIHRvdGFsX3N0ZXBzCmJveHBsb3QoZGFpbHlfYWN0aXZpdHlfY2xlYW4kdG90YWxfc3RlcHMsIAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBUb3RhbCBTdGVwcyIsCiAgICAgICAgeWxhYiA9ICJUb3RhbCBTdGVwcyIpCgojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9zdGVwcykKc3RkX2RldiA8LSByb3VuZChzZChkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9zdGVwcyksMikKCiMgSWRlbnRpZnkgb3V0bGllcnMKb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9zdGVwcykkb3V0CgojIENvdW50IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMKbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKCiMgQ3JlYXRlIHRoZSBsZWdlbmQgbGFiZWwgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46IiwgbWVkaWFuX3ZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCBzdGRfZGV2LCAKICAgICAgICAgICAgICAgICAgICAgICJcbk91dGxpZXJzOiIsIG51bV9vdXRsaWVycykKCiMgQWRkIHRoZSBsZWdlbmQgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbCwgcGNoID0gIiIsIGNvbCA9ICJibGFjayIsIGJ0eSA9ICJuIiwgY2V4ID0gMC44NSkKCmBgYAoKYGBge3J9CgojIFN0ZXBzIGF2ZXJhZ2VzIGJ5IElEcwpzdGVwc19kZiA8LSBkYWlseV9hY3Rpdml0eV9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2Vfc3RlcHMgPSBtZWFuKHRvdGFsX3N0ZXBzKSwgbWVkaWFuX3N0ZXBzID1tZWRpYW4odG90YWxfc3RlcHMpLCBuID0gbigpKQoKc3RlcHNfZGYKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBhdmVyYWdlIGNvbHVtbgphdF9sZWFzdF8xMGtfYXZnIDwtIHN1bShzdGVwc19kZiRhdmVyYWdlX3N0ZXBzID49IDEwMDAwKSAvIG5yb3coc3RlcHNfZGYpICogMTAwCmJldHdlZW5fNUtfMTBLX2F2ZyA8LSBzdW0oc3RlcHNfZGYkYXZlcmFnZV9zdGVwcyA+PSA1MDAwICYgc3RlcHNfZGYkYXZlcmFnZV9zdGVwcyA8IDEwMDAwKSAvIG5yb3coc3RlcHNfZGYpICogMTAwCmJlbG93XzVrX2F2ZyA8LSBzdW0oc3RlcHNfZGYkYXZlcmFnZV9zdGVwcyA8IDUwMDApIC8gbnJvdyhzdGVwc19kZikgKiAxMDAKCiMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIGZvciB0aGUgbWVkaWFuIGNvbHVtbgphdF9sZWFzdF8xMGtfbWVkIDwtIHN1bShzdGVwc19kZiRtZWRpYW5fc3RlcHMgPj0gMTAwMDApIC8gbnJvdyhzdGVwc19kZikgKiAxMDAKYmV0d2Vlbl81S18xMEtfbWVkIDwtIHN1bShzdGVwc19kZiRtZWRpYW5fc3RlcHMgPj0gNTAwMCAmIHN0ZXBzX2RmJG1lZGlhbl9zdGVwcyA8IDEwMDAwKSAvIG5yb3coc3RlcHNfZGYpICogMTAwCmJlbG93XzVrX21lZCA8LSBzdW0oc3RlcHNfZGYkbWVkaWFuX3N0ZXBzIDwgNTAwMCkgLyBucm93KHN0ZXBzX2RmKSAqIDEwMAoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgc3RlcHMgY2F0ZWdvcmllcwpwZXJjZW50YWdlX3N0ZXBzX2RmPC0gZGF0YS5mcmFtZSgKICBDYXRlZ29yeSA9IGMoIkJlbG93IDUsMDAwIiwgIkJldHdlZW4gNSwwMDAgYW5kIDEwLDAwMCIsICJBdCBsZWFzdCAxMCwwMDAiKSwKICBQZXJjZW50YWdlX0F2ZXJhZ2UgPSByb3VuZChjKGJlbG93XzVrX2F2ZywgYmV0d2Vlbl81S18xMEtfYXZnLCBhdF9sZWFzdF8xMGtfYXZnKSksCiAgUGVyY2VudGFnZV9NZWRpYW4gPSByb3VuZChjKGJlbG93XzVrX21lZCwgYmV0d2Vlbl81S18xMEtfbWVkLCBhdF9sZWFzdF8xMGtfbWVkKSkpCnBlcmNlbnRhZ2Vfc3RlcHNfZGYKCmBgYAoKCgoKCgpgYGB7cn0KIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV9zdGVwc19kZiRDYXRlZ29yeSA8LSBmYWN0b3IocGVyY2VudGFnZV9zdGVwc19kZiRDYXRlZ29yeSwgbGV2ZWxzID0gYygiQmVsb3cgNSwwMDAiLCAiQmV0d2VlbiA1LDAwMCBhbmQgMTAsMDAwIiwgIkF0IGxlYXN0IDEwLDAwMCIpKQoKIyBDcmVhdGUgYSBiYXIgcGxvdCB1c2luZyBnZ3Bsb3QKZ2dwbG90KHBlcmNlbnRhZ2Vfc3RlcHNfZGYsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBQZXJjZW50YWdlX0F2ZXJhZ2UpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiYmx1ZSIpICsKICBsYWJzKHggPSAiQXZlcmFnZSBUb3RhbCBTdGVwcyIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIHRpdGxlID0gIjU4JSBvZiBVc2VycyBBdmVyYWdlIDUsMDAwLTEwLDAwMCBTdGVwIERhaWx5IixzdWJ0aXRsZSA9ICJPbmx5IDIxJSBBY2hpZXZlIHRoZSAxMCwwMDAtU3RlcCBHb2FsIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAoUGVyY2VudGFnZV9BdmVyYWdlLCAiJSIpKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIpICsgCiAgeWxpbSgwLCAxMDApICsgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCgoKYGBgCgoKIyMjIyBUb3RhbCBEaXN0YW5jZTogVG90YWwga2lsb21ldGVycyB0cmFja2VkLgoKYGBge3J9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3IgdG90YWxfZGlzdGFuY2UKYm94cGxvdChkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9kaXN0YW5jZSwgCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFRvdGFsIERpc3RhbmNlIiwKICAgICAgICB5bGFiID0gIlRvdGFsIERpc3RhbmNlIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKQpzdGRfZGV2IDwtIHNkKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChtZWRpYW5fdmFsdWUsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCByb3VuZChzdGRfZGV2LCAyKSwgCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIpCgpgYGAKCgpgYGB7cn0KCiMgVG90YWwgZGlzdGFuY2UgYnkgSURzCnRfZGlzdGFuY2VfZGYgPC0gZGFpbHlfYWN0aXZpdHlfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX3RfZGlzdGFuY2UgPSBtZWFuKHRvdGFsX2Rpc3RhbmNlICksIG1lZGlhbl90X2Rpc3RhbmNlID1tZWRpYW4odG90YWxfZGlzdGFuY2UpLCBuID0gbigpKQoKdF9kaXN0YW5jZV9kZgpgYGAKCgpgYGB7cn0KIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBhdmVyYWdlIGNvbHVtbgphdF9sZWFzdF8xMF9hdmc8LSBzdW0odF9kaXN0YW5jZV9kZiRhdmVyYWdlX3RfZGlzdGFuY2U+PSAxMCkgLyBucm93KHRfZGlzdGFuY2VfZGYpICogMTAwCmJldHdlZW5fNV8xMF9hdmcgPC0gc3VtKHRfZGlzdGFuY2VfZGYkYXZlcmFnZV90X2Rpc3RhbmNlID49IDUgJiB0X2Rpc3RhbmNlX2RmJGF2ZXJhZ2VfdF9kaXN0YW5jZSA8IDEwKSAvIG5yb3codF9kaXN0YW5jZV9kZikgKiAxMDAKYmVsb3dfNV9hdmcgPC0gc3VtKHRfZGlzdGFuY2VfZGYkYXZlcmFnZV90X2Rpc3RhbmNlIDwgNSkgLyBucm93KHRfZGlzdGFuY2VfZGYpICogMTAwCgoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgZGlzdGFuY2UgY2F0ZWdvcmllcwpwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGY8LSBkYXRhLmZyYW1lKAogIENhdGVnb3J5ID0gYygiQmVsb3cgNSBrbSIsICJCZXR3ZWVuIDUgYW5kIDEwIGttIiwgIkF0IGxlYXN0IDEwIGttIiksCiAgUGVyY2VudGFnZV9BdmVyYWdlID0gcm91bmQoYyhiZWxvd181X2F2ZywgYmV0d2Vlbl81XzEwX2F2ZyAsIGF0X2xlYXN0XzEwX2F2ZykpKQpwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGYKCgoKIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV90X2Rpc3RhbmNlX2RmJENhdGVnb3J5IDwtIGZhY3RvcihwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGYkQ2F0ZWdvcnksIGxldmVscyA9IGMoIkJlbG93IDUga20iLCAiQmV0d2VlbiA1IGFuZCAxMCBrbSIsICJBdCBsZWFzdCAxMCBrbSIpKQoKCmBgYAoKYGBge3J9CiMgQ3JlYXRlIGEgYmFyIHBsb3QgdXNpbmcgZ2dwbG90CmdncGxvdChwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGYsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBQZXJjZW50YWdlX0F2ZXJhZ2UpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAicGluayIpICsKICBsYWJzKHggPSAiQXZlcmFnZSBUb3RhbCBEaXN0YW5jZSIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIHRpdGxlID0gIjU1JSBvZiBVc2VycyBBdmVyYWdlIDUtMTAgS2lsb21ldGVycyBEYWlseSIsc3VidGl0bGUgPSAiMTAsMDAwIHN0ZXBzIGlzIGFwcHJveGltYXRlbHkgZXF1YWwgdG8gY292ZXJpbmcgNSBtaWxlcyAob3IgOCBraWxvbWV0ZXJzKSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2VfQXZlcmFnZSwgIiUiKSksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIHlsaW0oMCwgMTAwKSArICB0aGVtZV9taW5pbWFsKCkgK3RoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCgpgYGAKCgojIyMjIFNlZGVudGFyeSBNaW51dGVzOiBUb3RhbCBtaW51dGVzIHNwZW50IGluIHNlZGVudGFyeSBhY3Rpdml0eS4KCmBgYHtyfQojIENyZWF0ZSBhIGJveHBsb3QgZm9yIHNlZGVudGFyeV9taW51dGVzCmJveHBsb3QoZGFpbHlfYWN0aXZpdHlfY2xlYW4kc2VkZW50YXJ5X21pbnV0ZXMsCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFNlZGVudGFyeSBNaW51dGVzIiwKICAgICAgICB5bGFiID0gIlNlZGVudGFyeSBNaW51dGVzIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHNlZGVudGFyeV9taW51dGVzKQpzdGRfZGV2IDwtIHNkKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHNlZGVudGFyeV9taW51dGVzKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHNlZGVudGFyeV9taW51dGVzKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChtZWRpYW5fdmFsdWUsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuU3RhbmRhcmQgRGV2aWF0aW9uOiIsIHJvdW5kKHN0ZF9kZXYsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQoKIyBBZGQgdGhlIGxlZ2VuZCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjgwKQoKCmBgYAoKCi0gVGhlc2UgYXJlIGhpZ2ggdmFsdWVzIGZvciBzZWRlbnRhcnkgbWludXRlcy4gRm9yIGluc3RhbmNlLCAxMDIwIG1pbnV0ZXMgaXMgZXF1aXZhbGVudCB0byAxNyBob3VycywgYW5kIDE0MDAgbWludXRlcyBpcyBlcXVpdmFsZW50IHRvIDI0IGhvdXJzLiBBZnRlciBwZXJmb3JtaW5nIGEgcXVpY2sgc2VhcmNoLCBpdCBzZWVtcyB0aGF0IHRoZQpbRml0Yml0IHVzZXMgMTQwMF0oaHR0cHM6Ly9jb21tdW5pdHkuZml0Yml0LmNvbS90NS9PdGhlci1DaGFyZ2UtVHJhY2tlcnMvc2VkZW50YXJ5LW1pbnV0ZXMvdGQtcC8zMzcyNjIxKSBhcyBkZWZhdWx0IGZvciBzZWRlbnRhcnkgbWludXRlcyB3aGVuIHRoZSBkZXZpY2UgaXMgbm90IHdvcm4gYW5kIGl0IGluY2x1ZGVzIHRoZSBzbGVlcGluZyB0aW1lLiAKU2VkZW50YXJ5TWludXRlcyBpcyB0b3RhbCBtaW51dGVzIHNwZW50IGluIHNlZGVudGFyeSBhY3Rpdml0eSBhY2NvcmRpbmcgdG8gdGhlIGRhdGEgZGljdGlvbmFyeS4gU2VlIG1ldGEgZGF0YSBzZWN0aW9uLiBUaGVyZWZvcmUsIHdlIG5lZWQgdG8gc3Vic3RyYWN0IHRoZSB0aW1lcyBzbGVlcGluZyB0byBvYnRhaW4gYW4gbW9yZSBhY2N1cmF0ZSBlc3RpbWF0ZSBvZiBkYWlseSBzZWRlbnRhcnkgbWludXRlcy4gCgpbU2xlZXAgdGltZSBpcyBub3QgY29uc2lkZXJlZCBzZWRlbnRhcnkgdGltZSwgc28gaXQgd2FzIHJlbW92ZWQgdG8gZGV0ZXJtaW5lIHRoZSB3YWtpbmcgZGF5IGFuZCB0byBhbGxvdyB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgZGF5IHNwZW50IHNlZGVudGFyeSB0byBiZSBjYWxjdWxhdGVkXShodHRwczovL3d3dy5tZHBpLmNvbS8xNjYwLTQ2MDEvMTgvOC8zOTE0KQoKCmBgYHtyfQojIENoZWNrIHNlZGVudGFyeV9taW51dGVzIHN0YXRzCmRhaWx5X2FjdGl2aXR5X2NsZWFuJHNlZGVudGFyeV9taW51dGVzICU+JSBzdW1tYXJ5KCkKCmBgYAoKYGBge3J9Cm91dGxpZXJzCmBgYAoKCmBgYHtyfQojIENvdW50IGVudHJpZXMgd2hlcmUgc2VkZW50YXJ5IG1pbnV0ZXMgZXF1YWwgMTQ0MApjb3VudF8xNDQwIDwtIHN1bShkYWlseV9hY3Rpdml0eV9jbGVhbiRzZWRlbnRhcnlfbWludXRlcyA9PSAxNDQwKQoKIyBPdXRwdXQgdGhlIGNvdW50CmNvdW50XzE0NDAKCmBgYApgYGB7cn0KIyBSZW1vdmUgcm93cyB3aXRoIHNlZGVudGFyeSBtaW51dGVzIGVxdWFsIHRvIHRoZSBkZWZhdWx0IHZhbHVlICgxNDQwKSBhbmQgb3V0bGllcnMKCmRhaWx5X2FjdGl2aXR5X2NsZWFuIDwtIGZpbHRlcihkYWlseV9hY3Rpdml0eV9jbGVhbiwgIShzZWRlbnRhcnlfbWludXRlcyAlaW4lIGMoMCwgMiwgMTMsIDE0NDApKSkKCmBgYAoKCgpgYGB7cn0KCgojIFJlbmFtZSB0aGUgY29sdW1uCmRhaWx5X3NsZWVwX2NsZWFuIDwtIHJlbmFtZShkYWlseV9zbGVlcF9jbGVhbiwgYWN0aXZpdHlfZGF0ZSA9IHNsZWVwX2RheSkKCiMgSm9pbiB0aGUgZGF0YXNldHMKam9pbmVkX2FjdGl2aXR5X3NsZWVwIDwtIGlubmVyX2pvaW4oZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGRhaWx5X3NsZWVwX2NsZWFuLCBieSA9IGMoImlkIiwgImFjdGl2aXR5X2RhdGUiKSkKCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgKICAiXG4iLAogICJNaXNzaW5nIHZhbHVlczoiLAogIHN1bShpcy5uYShqb2luZWRfYWN0aXZpdHlfc2xlZXAgKSksCiAgIlxuIiwKICAiRHVwbGljYXRlIHZhbHVlczoiLAogIHN1bShkdXBsaWNhdGVkKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCApKSwKICAiXG4iLAogICJVbmlxdWUgSWRzOiIsCiAgbl9kaXN0aW5jdChqb2luZWRfYWN0aXZpdHlfc2xlZXAgJGlkKQopCgpgYGAKCgpgYGB7cn0KIyBDcmVhdGUgYSBkZXJpdmVkIGNvbHVtbiBmb3Igc2VkZW50YXJ5IG1pbnV0ZXMgdGhhdCBkb2VzIG5vdCBpbmNsdWRlIHNsZWVwIHRpbWUKam9pbmVkX2FjdGl2aXR5X3NsZWVwIDwtIGpvaW5lZF9hY3Rpdml0eV9zbGVlcCAlPiUKICBtdXRhdGUoCiAgICBzZWRlbnRhcnlfbWluX2F3YWtlID0gc2VkZW50YXJ5X21pbnV0ZXMgLSB0b3RhbF9taW51dGVzX2FzbGVlcCwKICAgIHNlZGVudGFyeV9ob3Vyc19hd2FrZSA9IHNlZGVudGFyeV9taW5fYXdha2UgLyA2MCwKICAgIHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYgPSAoc2VkZW50YXJ5X21pbnV0ZXMgLSBzZWRlbnRhcnlfbWluX2F3YWtlKSAvIHNlZGVudGFyeV9taW51dGVzICogMTAwCiAgKQoKYGBgCgoKCmBgYHtyfQoKIyBMZXQgdXMgY2hlY2sgdGhlIHBlcmNlbnRhZ2UgZGlmZmVyZW5jZSBvZiBzZWRlbnRhcnlfbWludXRlcyBhbmQgdGhlIG5ldyBjb2x1bW4gInNlZGVudGFyeV9taW5fYXdha2UKCiMgQ3JlYXRlIGEgYm94cGxvdCBmb3Igc2VkZW50YXJ5X3BlcmNlbnRhZ2VfZGlmZgpib3hwbG90KGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfcGVyY2VudGFnZV9kaWZmLAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBTZWRlbnRhcnkgUGVyY2VudGFnZSBEaWZmZXJlbmNlIiwKICAgICAgICB5bGFiID0gIlNlZGVudGFyeSBQZXJjZW50YWdlIERpZmZlcmVuY2UiKQoKIyBDYWxjdWxhdGUgdGhlIG1lZGlhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uCm1lZGlhbl92YWx1ZSA8LSBtZWRpYW4oam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpCnN0ZF9kZXYgPC0gc2Qoam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpCgojIElkZW50aWZ5IG91dGxpZXJzCm91dGxpZXJzIDwtIGJveHBsb3Quc3RhdHMoam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpJG91dAoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIG91dGxpZXJzCm51bV9vdXRsaWVycyA8LSBsZW5ndGgob3V0bGllcnMpCgojIENyZWF0ZSB0aGUgbGVnZW5kIGxhYmVsIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZF9sYWJlbCA8LSBwYXN0ZSgiTWVkaWFuOiIsIHJvdW5kKG1lZGlhbl92YWx1ZSwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5TdGFuZGFyZCBEZXZpYXRpb246Iiwgcm91bmQoc3RkX2RldiwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIsIGNleCA9IDAuODApCgpgYGAKLSBUaGUgc2VkZW50YXJ5IHBlcmNlbnRhZ2UgZGlmZmVyZW5jZSBoYXMgYSBtZWRpYW4gdmFsdWUgb2YgNTkuOTUlLCBpbmRpY2F0aW5nIGEgc2lnbmlmaWNhbnQgZGlzdGluY3Rpb24gYmV0d2VlbiBzZWRlbnRhcnlfbWludXRlcyBhbmQgc2VkZW50YXJ5X21pbl9hd2FrZS4gVGhpcyBzdWdnZXN0IHRoYXQgdGhlIG9yaWdpbmFsIGNvbHVtbiAic2VkZW50YXJ5X21pbnV0ZXMiIGluY2x1ZGVkIHRoZSB0aW1lIGFzbGVlcC4KCgoKCgoKYGBge3J9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3Igc2VkZW50YXJ5X21pbl9hd2FrZQpib3hwbG90KGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfbWluX2F3YWtlLAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBTZWRlbnRhcnkgTWludXRlcyBBd2FrZSIsCiAgICAgICAgeWxhYiA9ICJTZWRlbnRhcnkgTWludXRlcyBBd2FrZSIpCgojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihqb2luZWRfYWN0aXZpdHlfc2xlZXAkc2VkZW50YXJ5X21pbl9hd2FrZSkKc3RkX2RldiA8LSBzZChqb2luZWRfYWN0aXZpdHlfc2xlZXAkc2VkZW50YXJ5X21pbl9hd2FrZSkKCiMgSWRlbnRpZnkgb3V0bGllcnMKb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhqb2luZWRfYWN0aXZpdHlfc2xlZXAkc2VkZW50YXJ5X21pbl9hd2FrZSkkb3V0CgojIENvdW50IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMKbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKCiMgQ3JlYXRlIHRoZSBsZWdlbmQgbGFiZWwgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46Iiwgcm91bmQobWVkaWFuX3ZhbHVlLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCByb3VuZChzdGRfZGV2LCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcbk91dGxpZXJzOiIsIG51bV9vdXRsaWVycykKCiMgQWRkIHRoZSBsZWdlbmQgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbCwgcGNoID0gIiIsIGNvbCA9ICJibGFjayIsIGJ0eSA9ICJuIiwgY2V4ID0gMC44MCkKCmBgYAotIE9ic2VydmF0aW9uOiBUaGVyZSBhcHBlYXJzIHRvIGJlIGFuIGluY29uc2lzdGVuY3kgaW4gdGhlIGRhdGEuIFRoZSBzZWRlbnRhcnlfbWludXRlcyB2YWx1ZSBpcyBzbWFsbGVyIHRoYW4gdGhlIHRvdGFsX21pbnV0ZXNfYXNsZWVwIHZhbHVlLCB3aGljaCBpcyB1bmV4cGVjdGVkLiAKCgpgYGB7cn0KIyBDb3VudCB0aGUgbnVtYmVyIG9mIGNhc2VzIHdoZXJlIHNlZGVudGFyeV9taW51dGVzIGlzIHNtYWxsZXIgdGhhbiB0b3RhbF9taW51dGVzX2FzbGVlcApjb3VudCA8LSBzdW0oam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9taW51dGVzIDwgam9pbmVkX2FjdGl2aXR5X3NsZWVwJHRvdGFsX21pbnV0ZXNfYXNsZWVwKQoKIyBQcmludCB0aGUgY291bnQKY291bnQKCgpgYGAKCgpgYGB7cn0KIyBTdWJzZXQgdGhlIGRhdGFzZXQKc3Vic2V0X2RhdGEgPC0gam9pbmVkX2FjdGl2aXR5X3NsZWVwW2pvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfbWludXRlcyA8IGpvaW5lZF9hY3Rpdml0eV9zbGVlcCR0b3RhbF9taW51dGVzX2FzbGVlcCwgXQoKIyBWaWV3IHRoZSBzdWJzZXR0ZWQgZGF0YQpzdWJzZXRfZGF0YQoKYGBgCgoKCmBgYHtyfQojIENoZWNrIGNvbHVtbiBuYW1lcyBvZiB0aGUgc3Vic2V0dGVkIGRhdGEKc3Vic2V0X2RhdGEgJT4lCnNlbGVjdChzZWRlbnRhcnlfbWludXRlcywgdG90YWxfbWludXRlc19hc2xlZXAsIHNlZGVudGFyeV9taW5fYXdha2UsIGNhbG9yaWVzLGlkLCBhY3Rpdml0eV9kYXRlLCB0b3RhbF9zdGVwcywgdG90YWxfZGlzdGFuY2UsIHZlcnlfYWN0aXZlX21pbnV0ZXMgKQpgYGAKCgpgYGB7cn0KZGltKHN1YnNldF9kYXRhKQpgYGAKYGBge3J9CmRpbShqb2luZWRfYWN0aXZpdHlfc2xlZXApCmBgYAoKCgpgYGB7cn0KIyBVc2UgYW50aV9qb2luKCkgdG8gcmV0dXJuIGEgbmV3IGRhdGFzZXQgdGhhdCBpbmNsdWRlcyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCBkYXRhc2V0IGV4Y2VwdCBmb3IgdGhlIHJvd3MgdGhhdCBoYXZlIGEgbWF0Y2ggaW4gdGhlIHNlY29uZCBkYXRhc2V0LgogY2xlYW5fc3Vic2V0PC0gYW50aV9qb2luKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCwgc3Vic2V0X2RhdGEpCmRpbShjbGVhbl9zdWJzZXQpCmBgYAoKCmBgYHtyfQojIENyZWF0ZSBhIGJveHBsb3QgZm9yIHNlZGVudGFyeV9taW5fYXdha2UKYm94cGxvdChjbGVhbl9zdWJzZXQkc2VkZW50YXJ5X21pbl9hd2FrZSwKICAgICAgICBtYWluID0gIkJveHBsb3Qgb2YgU2VkZW50YXJ5IE1pbnV0ZXMgQXdha2UiLAogICAgICAgIHlsYWIgPSAiU2VkZW50YXJ5IE1pbnV0ZXMgQXdha2UiKQoKIyBDYWxjdWxhdGUgdGhlIG1lZGlhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uCm1lZGlhbl92YWx1ZSA8LSBtZWRpYW4oY2xlYW5fc3Vic2V0JHNlZGVudGFyeV9taW5fYXdha2UpCnN0ZF9kZXYgPC0gc2QoY2xlYW5fc3Vic2V0JHNlZGVudGFyeV9taW5fYXdha2UpCgojIElkZW50aWZ5IG91dGxpZXJzCm91dGxpZXJzIDwtIGJveHBsb3Quc3RhdHMoY2xlYW5fc3Vic2V0JHNlZGVudGFyeV9taW5fYXdha2UpJG91dAoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIG91dGxpZXJzCm51bV9vdXRsaWVycyA8LSBsZW5ndGgob3V0bGllcnMpCgojIENyZWF0ZSB0aGUgbGVnZW5kIGxhYmVsIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZF9sYWJlbCA8LSBwYXN0ZSgiTWVkaWFuOiIsIHJvdW5kKG1lZGlhbl92YWx1ZSwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5TdGFuZGFyZCBEZXZpYXRpb246Iiwgcm91bmQoc3RkX2RldiwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIsIGNleCA9IDAuODApCgpgYGAKT2JzZXJ2YXRpb246IEJ5IGVsaW1pbmF0aW5nIG5lZ2F0aXZlIHZhbHVlcyBmcm9tICJzZWRlbnRhcnlfbWluX2F3YWtlLCIgdGhlIHJlc3VsdGluZyB2YWx1ZXMgbm93IHJlZmxlY3QgYSBtb3JlIHJlYWxpc3RpYyBzY2VuYXJpby4KCgpgYGB7cn0KIyBUb3RhbCBzZWRlbnRhcnkgbWludXRlcyBhd2FrZSBieSBJRHMKdF9zZWRlbnRhcnlfZGYgPC0gY2xlYW5fc3Vic2V0ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9zZWRlbnRhcnlfbWluX2F3YWtlID0gbWVhbihzZWRlbnRhcnlfbWluX2F3YWtlKSwKICAgICAgICAgICAgbWVkaWFuX3NlZGVudGFyeV9taW5fYXdha2UgPSBtZWRpYW4oc2VkZW50YXJ5X21pbl9hd2FrZSksIG4gPSBuKCkpCgp0X3NlZGVudGFyeV9kZgoKYGBgCgoKCmBgYHtyfQpkYXRhc2V0IDwtIHRfc2VkZW50YXJ5X2RmCmNvbHVtbiA8LSAiYXZlcmFnZV9zZWRlbnRhcnlfbWluX2F3YWtlIgpuZXdfY2F0ZWdvcmllcyA8LSBjKCJCZWxvdyAyMDAgbWludXRlcyIsICJCZXR3ZWVuIDIwMCBhbmQgNDAwIG1pbnV0ZXMiLCAiQXQgbGVhc3QgNDAwIG1pbnV0ZXMiKQoKIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBhdmVyYWdlIGNvbHVtbgpiZWxvd18yMDBfYXZnIDwtIHN1bShkYXRhc2V0W1tjb2x1bW5dXSA8IDIwMCkgLyBucm93KGRhdGFzZXQpICogMTAwCmJldHdlZW5fMjAwXzQwMF9hdmcgPC0gc3VtKGRhdGFzZXRbW2NvbHVtbl1dID49IDIwMCAmIGRhdGFzZXRbW2NvbHVtbl1dIDw9IDQwMCkgLyBucm93KGRhdGFzZXQpICogMTAwCmF0X2xlYXN0XzQwMF9hdmcgPC0gc3VtKGRhdGFzZXRbW2NvbHVtbl1dID49IDQwMCkgLyBucm93KGRhdGFzZXQpICogMTAwCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBjYXRlZ29yaWVzCnBlcmNlbnRhZ2Vfc2VkZW50YXJ5X2F3YWtlX2RmIDwtIGRhdGEuZnJhbWUoCiAgQ2F0ZWdvcnkgPSBuZXdfY2F0ZWdvcmllcywKICBQZXJjZW50YWdlX0F2ZXJhZ2UgPSByb3VuZChjKGJlbG93XzIwMF9hdmcsIGJldHdlZW5fMjAwXzQwMF9hdmcsIGF0X2xlYXN0XzQwMF9hdmcpKQopCgojIENvbnZlcnQgQ2F0ZWdvcnkgdG8gYSBmYWN0b3Igd2l0aCBjdXN0b20gZmFjdG9yIGxldmVscwpwZXJjZW50YWdlX3NlZGVudGFyeV9hd2FrZV9kZiRDYXRlZ29yeSA8LSBmYWN0b3IocGVyY2VudGFnZV9zZWRlbnRhcnlfYXdha2VfZGYkQ2F0ZWdvcnksIGxldmVscyA9IG5ld19jYXRlZ29yaWVzKQoKcGVyY2VudGFnZV9zZWRlbnRhcnlfYXdha2VfZGYKCgoKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBiYXIgcGxvdCB1c2luZyBnZ3Bsb3QKZ2dwbG90KHBlcmNlbnRhZ2Vfc2VkZW50YXJ5X2F3YWtlX2RmLCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gUGVyY2VudGFnZV9BdmVyYWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyYXkiKSArCiAgbGFicyh4ID0gIkF2ZXJhZ2UgVG90YWwgU2VkZW50YXJ5IE1pbiBBd2FrZSIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIAogICAgICAgdGl0bGUgPSAiNDglIG9mIFVzZXJzIEhhdmUgYW4gQXZlcmFnZSBvZiBhdCBMZWFzdCA0MDAgRGFpbHkgU2VkZW50YXJ5IE1pbnV0ZXMgV2hpbGUgQXdha2UiLAogICAgICAgc3VidGl0bGUgPSAiMjAwIE1pbnV0ZXMgYXJlIDMgaG91cnMgYW5kIDIwIG1pbnV0ZXM7IDQwMCBtaW4gYXJlIDYgaG91cnMgYW5kIDQwIG1pbiIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2VfQXZlcmFnZSwgIiUiKSksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIHlsaW0oMCwgMTAwKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCmBgYAoKSW4gYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgVS5TLiBhZHVsdHMsIG92ZXIgdHdvLXRoaXJkcyBzcGVudCA2ICsgaG91cnMvZGF5IHNpdHRpbmcsIGFuZCBtb3JlIHRoYW4gaGFsZiBkaWQgbm90IG1lZXQgdGhlIHJlY29tbWVuZGVkIDE1MCBtaW4vd2VlayBvZiBwaHlzaWNhbCBhY3Rpdml0eS4gVGhlIHN0dWR5IGRpc2NvdmVyZWQgdGhhdCBwcm9sb25nZWQgc2l0dGluZyBmb3IgNisgaG91cnMvZGF5IHdhcyBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIGJvZHkgZmF0IHBlcmNlbnRhZ2VzLiBXaGlsZSBleGNlZWRpbmcgMTUwIG1pbi93ZWVrIG9mIHBoeXNpY2FsIGFjdGl2aXR5IHdhcyBsaW5rZWQgdG8gbG93ZXIgYm9keSBmYXQgcGVyY2VudGFnZXMsIGFjaGlldmluZyByZWNvbW1lbmRlZCBhY3Rpdml0eSBsZXZlbHMgbWF5IG5vdCBmdWxseSBvZmZzZXQgdGhlIGluY3JlYXNlZCBib2R5IGZhdCBmcm9tIHByb2xvbmdlZCBzaXR0aW5nLiAKCkppbmd3ZW4gTGlhbywgTWluIEh1LCBLZWxsaWUgSW1tLCBDbGlmdG9uIEouIEhvbG1lcywgSmllIFpodSwgQ2hhbyBDYW8sIExpbiBZYW5nLiBBc3NvY2lhdGlvbiBvZiBkYWlseSBzaXR0aW5nIHRpbWUgYW5kIGxlaXN1cmUtdGltZSBwaHlzaWNhbCBhY3Rpdml0eSB3aXRoIGJvZHkgZmF0IGFtb25nIFUuUy4gYWR1bHRzLiBKb3VybmFsIG9mIFNwb3J0IGFuZCBIZWFsdGggU2NpZW5jZSwgMjAyMi4gSVNTTiAyMDk1LTI1NDYuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouanNocy4yMDIyLjEwLjAwMS4gKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzIwOTUyNTQ2MjIwMDEwMTYpCgoKCiMjIyMgQ2Fsb3JpZXM6VG90YWwgZXN0aW1hdGVkIGVuZXJneSBleHBlbmRpdHVyZSAoaW4ga2lsb2NhbG9yaWVzKS4gCmBgYHtyfQojIENyZWF0ZSBhIGJveHBsb3QgZm9yIGNhbG9yaWVzCmJveHBsb3QoZGFpbHlfYWN0aXZpdHlfY2xlYW4kY2Fsb3JpZXMsIAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBDYWxvcmllcyIsCiAgICAgICAgeWxhYiA9ICJDYWxvcmllcyIpCgojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihkYWlseV9hY3Rpdml0eV9jbGVhbiRjYWxvcmllcykKc3RkX2RldiA8LSByb3VuZChzZChkYWlseV9hY3Rpdml0eV9jbGVhbiRjYWxvcmllcyksMikKCiMgSWRlbnRpZnkgb3V0bGllcnMKb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhkYWlseV9hY3Rpdml0eV9jbGVhbiRjYWxvcmllcykkb3V0CgojIENvdW50IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMKbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKCiMgQ3JlYXRlIHRoZSBsZWdlbmQgbGFiZWwgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46IiwgbWVkaWFuX3ZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCBzdGRfZGV2LCAKICAgICAgICAgICAgICAgICAgICAgICJcbk91dGxpZXJzOiIsIG51bV9vdXRsaWVycykKCiMgQWRkIHRoZSBsZWdlbmQgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbCwgcGNoID0gIiIsIGNvbCA9ICJibGFjayIsIGJ0eSA9ICJuIiwgY2V4ID0gMC44NSkKCmBgYAoKCmBgYHtyfQpvdXRsaWVycwpgYGAKCmBgYHtyfQojIENhbG9yaWVzIGF2ZXJhZ2VzIGJ5IElEcwpjYWxvcmllc19kZiA8LSBkYWlseV9hY3Rpdml0eV9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfY2Fsb3JpZXMgPSBtZWFuKGNhbG9yaWVzKSwgbWVkaWFuX2NhbG9yaWVzID0gbWVkaWFuKGNhbG9yaWVzKSkKCmNhbG9yaWVzX2RmCgpgYGAKCgoKCmBgYHtyfQojIENhbGN1bGF0ZSBwZXJjZW50YWdlcyBmb3IgdGhlIGF2ZXJhZ2UgY29sdW1uCmJlbG93XzE2MDBfYXZnIDwtIHN1bShjYWxvcmllc19kZiRhdmVyYWdlX2NhbG9yaWVzIDwgMTYwMCkgLyBucm93KGNhbG9yaWVzX2RmKSAqIDEwMApiZXR3ZWVuXzE2MDBfMjIwMF9hdmcgPC0gc3VtKGNhbG9yaWVzX2RmJGF2ZXJhZ2VfY2Fsb3JpZXMgPj0gMTYwMCAmIGNhbG9yaWVzX2RmJGF2ZXJhZ2VfY2Fsb3JpZXMgPCAyMjAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCmJldHdlZW5fMjIwMF8zMDAwX2F2ZyA8LSBzdW0oY2Fsb3JpZXNfZGYkYXZlcmFnZV9jYWxvcmllcyA+PSAyMjAwICYgY2Fsb3JpZXNfZGYkYXZlcmFnZV9jYWxvcmllcyA8IDMwMDApIC8gbnJvdyhjYWxvcmllc19kZikgKiAxMDAKYXRfbGVhc3RfMzAwMF9hdmcgPC0gc3VtKGNhbG9yaWVzX2RmJGF2ZXJhZ2VfY2Fsb3JpZXMgPj0gMzAwMCkgLyBucm93KGNhbG9yaWVzX2RmKSAqIDEwMAoKIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBtZWRpYW4gY29sdW1uCmJlbG93XzE2MDBfbWVkIDwtIHN1bShjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPCAxNjAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCmJldHdlZW5fMTYwMF8yMjAwX21lZCA8LSBzdW0oY2Fsb3JpZXNfZGYkbWVkaWFuX2NhbG9yaWVzID49IDE2MDAgJiBjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPCAyMjAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCmJldHdlZW5fMjIwMF8zMDAwX21lZCA8LSBzdW0oY2Fsb3JpZXNfZGYkbWVkaWFuX2NhbG9yaWVzID49IDIyMDAgJiBjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPCAzMDAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCmF0X2xlYXN0XzMwMDBfbWVkIDwtIHN1bShjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPj0gMzAwMCkgLyBucm93KGNhbG9yaWVzX2RmKSAqIDEwMAoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgY2Fsb3JpZXMgY2F0ZWdvcmllcwpwZXJjZW50YWdlX2NhbG9yaWVzX2RmIDwtIGRhdGEuZnJhbWUoCiAgQ2F0ZWdvcnkgPSBjKCJCZWxvdyAxLDYwMCIsICJCZXR3ZWVuIDEsNjAwIGFuZCAyLDIwMCIsICJCZXR3ZWVuIDIsMjAwIGFuZCAzLDAwMCIsICJBdCBsZWFzdCAzLDAwMCIpLAogIFBlcmNlbnRhZ2VfQXZlcmFnZSA9IHJvdW5kKGMoYmVsb3dfMTYwMF9hdmcsIGJldHdlZW5fMTYwMF8yMjAwX2F2ZywgYmV0d2Vlbl8yMjAwXzMwMDBfYXZnLCBhdF9sZWFzdF8zMDAwX2F2ZykpLAogIFBlcmNlbnRhZ2VfTWVkaWFuID0gcm91bmQoYyhiZWxvd18xNjAwX21lZCwgYmV0d2Vlbl8xNjAwXzIyMDBfbWVkLCBiZXR3ZWVuXzIyMDBfMzAwMF9tZWQsIGF0X2xlYXN0XzMwMDBfbWVkKSkKKQoKIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV9jYWxvcmllc19kZiRDYXRlZ29yeSA8LSBmYWN0b3IocGVyY2VudGFnZV9jYWxvcmllc19kZiRDYXRlZ29yeSwgbGV2ZWxzID0gYygiQmVsb3cgMSw2MDAiLCAiQmV0d2VlbiAxLDYwMCBhbmQgMiwyMDAiLCAiQmV0d2VlbiAyLDIwMCBhbmQgMywwMDAiLCAiQXQgbGVhc3QgMywwMDAiKSkKCnBlcmNlbnRhZ2VfY2Fsb3JpZXNfZGYKCmBgYAoKCmBgYHtyfQojIENyZWF0ZSBhIGJhciBwbG90IHVzaW5nIGdncGxvdApnZ3Bsb3QocGVyY2VudGFnZV9jYWxvcmllc19kZiwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IFBlcmNlbnRhZ2VfQXZlcmFnZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJyZWQiKSArCiAgbGFicyh4ID0gIkNhbG9yaWUgQ2F0ZWdvcmllcyIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIAogICAgICAgdGl0bGUgPSAiNDIlIG9mIFVzZXJzIEhhdmUgYW4gQXZlcmFnZSBEYWlseSBDYWxvcmllIEV4cGVuZGl0dXJlIEJldHdlZW4gMSw2MDAgYW5kIDIsMjAwLiIsCiAgICAgICBzdWJ0aXRsZSA9ICJNb3N0IGZlbWFsZXMgcmVxdWlyZSAxLDYwMCB0byAyLDIwMCBjYWxvcmllcyBwZXIgZGF5LCBhcyBwZXIgdGhlIERpZXRhcnkgR3VpZGVsaW5lcyBmb3IgQW1lcmljYW5zLCAyMDIwLTIwMjUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChQZXJjZW50YWdlX0F2ZXJhZ2UsICIlIikpLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIikgKyAKICB5bGltKDAsIDEwMCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCgpgYGAKCgoiRmVtYWxlcyBhZ2VzIDE5IHRocm91Z2ggMzAgcmVxdWlyZSBhYm91dCAxLDgwMCB0byAyLDQwMCBjYWxvcmllcyBhIGRheS4gTWFsZXMgaW4gdGhpcyBhZ2UgZ3JvdXAgaGF2ZSBoaWdoZXIgY2Fsb3JpZSBuZWVkcyBvZiBhYm91dCAyLDQwMCB0byAzLDAwMCBhIGRheS4gQ2Fsb3JpZSBuZWVkcyBmb3IgYWR1bHRzIGFnZXMgMzEgdGhyb3VnaCA1OSBhcmUgZ2VuZXJhbGx5IGxvd2VyOyBtb3N0IGZlbWFsZXMgcmVxdWlyZSBhYm91dCAxLDYwMCB0byAyLDIwMCBjYWxvcmllcyBhIGRheSBhbmQgbWFsZXMgcmVxdWlyZSBhYm91dCAyLDIwMCB0byAzLDAwMCBjYWxvcmllcyBhIGRheS4iIAoKVS5TLiBEZXBhcnRtZW50IG9mIEFncmljdWx0dXJlIGFuZCBVLlMuIERlcGFydG1lbnQgb2YgSGVhbHRoIGFuZCBIdW1hbiBTZXJ2aWNlcy4gRGlldGFyeSBHdWlkZWxpbmVzIGZvciBBbWVyaWNhbnMsIDIwMjAtMjAyNS4gOXRoIEVkaXRpb24uIERlY2VtYmVyIDIwMjAuIEF2YWlsYWJsZSBhdCBEaWV0YXJ5R3VpZGVsaW5lcy5nb3YvICAKCgoKIyMjIyBJbnRlbnNpdHkgTWludXRlczogVGltZSBzcGVudCBpbiBvbmUgb2YgZm91ciBpbnRlbnNpdHkgY2F0ZWdvcmllcy4KCi0gVmVyeUFjdGl2ZU1pbnV0ZXM6IFRvdGFsIG1pbnV0ZXMgc3BlbnQgaW4gdmVyeSBhY3RpdmUgYWN0aXZpdHkKCi0gRmFpcmx5QWN0aXZlTWludXRlczogVG90YWwgbWludXRlcyBzcGVudCBpbiBtb2RlcmF0ZSBhY3Rpdml0eQoKLSBMaWdodGx5QWN0aXZlTWludXRlczogVG90YWwgbWludXRlcyBzcGVudCBpbiBsaWdodCBhY3Rpdml0eQoKLSBTZWRlbnRhcnlNaW51dGVzOiBUb3RhbCBtaW51dGVzIHNwZW50IGluIHNlZGVudGFyeSBhY3Rpdml0eQoKCgpgYGB7cn0KYWN0aXZpdHlfbWludXRlc19kZiA8LSBkYWlseV9hY3Rpdml0eV9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKAogICAgYXZlcmFnZV92ZXJ5X2FjdGl2ZV9taW51dGVzID0gbWVhbih2ZXJ5X2FjdGl2ZV9taW51dGVzKSwKICAgIGF2ZXJhZ2VfZmFpcmx5X2FjdGl2ZV9taW51dGVzID0gbWVhbihmYWlybHlfYWN0aXZlX21pbnV0ZXMpLAogICAgYXZlcmFnZV9saWdodGx5X2FjdGl2ZV9taW51dGVzID0gbWVhbihsaWdodGx5X2FjdGl2ZV9taW51dGVzKSwKICAgIGF2ZXJhZ2Vfc2VkZW50YXJ5X21pbnV0ZXMgPSBtZWFuKHNlZGVudGFyeV9taW51dGVzKQogICkKCmFjdGl2aXR5X21pbnV0ZXNfZGYKCgpgYGAKCgpgYGB7ciBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD04fQoKIyBEZWZpbmUgdGhlIGN1c3RvbSBvcmRlciBvZiBsZWdlbmQgaXRlbXMKY3VzdG9tX29yZGVyIDwtIGMoICJWZXJ5IEFjdGl2ZSIsICJGYWlybHkgQWN0aXZlIiwgIkxpZ2h0bHkgQWN0aXZlIiwgIlNlZGVudGFyeSIpCgojIENyZWF0ZSB0aGUgc3RhY2tlZCBiYXIgcGxvdApnZ3Bsb3QoYWN0aXZpdHlfbWludXRlc19kZiwgYWVzKHkgPSBpZCkpICsKICBnZW9tX2JhcihhZXMoeCA9IGF2ZXJhZ2Vfc2VkZW50YXJ5X21pbnV0ZXMsIGZpbGwgPSAiU2VkZW50YXJ5IiksIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSkgKwogIGdlb21fYmFyKGFlcyh4ID0gYXZlcmFnZV9saWdodGx5X2FjdGl2ZV9taW51dGVzLCBmaWxsID0gIkxpZ2h0bHkgQWN0aXZlIiksIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSkgKwogIGdlb21fYmFyKGFlcyh4ID0gYXZlcmFnZV9mYWlybHlfYWN0aXZlX21pbnV0ZXMsIGZpbGwgPSAiRmFpcmx5IEFjdGl2ZSIpLCBzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsKICBnZW9tX2JhcihhZXMoeCA9IGF2ZXJhZ2VfdmVyeV9hY3RpdmVfbWludXRlcywgZmlsbCA9ICJWZXJ5IEFjdGl2ZSIpLCBzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsKICB4bGFiKCJNaW51dGVzIikgKwogIHlsYWIoIklEIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgQWN0aXZpdHkgTWludXRlcyBieSBJRCIpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIiIsIHZhbHVlcyA9IGMoIlZlcnkgQWN0aXZlIiA9ICJyZWQiLCAiRmFpcmx5IEFjdGl2ZSIgPSAib3JhbmdlIiwgIkxpZ2h0bHkgQWN0aXZlIiA9ICJsaWdodGdyZWVuIiwgIlNlZGVudGFyeSIgPSAibGlnaHRibHVlIiksIGJyZWFrcyA9IGN1c3RvbV9vcmRlcikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpIAoKICAKCgoKCgoKYGBgCgoKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBmb3IgZWFjaCBjb2x1bW4KYXZlcmFnZXMgPC0gY29sTWVhbnMoYWN0aXZpdHlfbWludXRlc19kZlssIGMoImF2ZXJhZ2VfdmVyeV9hY3RpdmVfbWludXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmVyYWdlX2ZhaXJseV9hY3RpdmVfbWludXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmVyYWdlX2xpZ2h0bHlfYWN0aXZlX21pbnV0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXZlcmFnZV9zZWRlbnRhcnlfbWludXRlcyIpXSkKCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBhdmVyYWdlCnRvdGFsX2F2ZXJhZ2UgPC0gc3VtKGF2ZXJhZ2VzKQoKIyBDYWxjdWxhdGUgdGhlIHByb3BvcnRpb25zCnByb3BvcnRpb25zIDwtIGF2ZXJhZ2VzIC8gdG90YWxfYXZlcmFnZQoKIyBDcmVhdGUgdGhlIG5ldyBkYXRhZnJhbWUgd2l0aCBtb2RpZmllZCByb3cgbmFtZXMKb3ZlcmFsbF9hdmVyYWdlX2RmPC0gZGF0YS5mcmFtZShBdmVyYWdlID0gYXZlcmFnZXMsCiAgICAgICAgICAgICAgICAgICAgIFBlcmNlbnRhZ2UgPSBwcm9wb3J0aW9ucyAqIDEwMCkKCiMgTW9kaWZ5IHRoZSByb3cgbmFtZXMKcm93X25hbWVzIDwtIGMoIlZlcnkgQWN0aXZlIiwgIkZhaXJseSBBY3RpdmUiLCAiTGlnaHRseSBBY3RpdmUiLCAiU2VkZW50YXJ5IikKcm93Lm5hbWVzKG92ZXJhbGxfYXZlcmFnZV9kZikgPC0gcm93X25hbWVzCgojIFByaW50IHRoZSBuZXcgZGF0YWZyYW1lCm92ZXJhbGxfYXZlcmFnZV9kZgoKCmBgYAoKCgoKCgoKYGBge3IgIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQoKZ2dwbG90KG92ZXJhbGxfYXZlcmFnZV9kZiwgYWVzKHggPSBQZXJjZW50YWdlLCB5ID0gcmVvcmRlcihyb3cubmFtZXMob3ZlcmFsbF9hdmVyYWdlX2RmKSwgUGVyY2VudGFnZSksIGZpbGwgPSByb3cubmFtZXMob3ZlcmFsbF9hdmVyYWdlX2RmKSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjcsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKFBlcmNlbnRhZ2UpLCAiJSIpKSwgaGp1c3QgPSAtMC4yLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArCiAgeWxhYigiTWludXRlcyBJbnRlbnNpdHkiKSArCiAgeGxhYigiUGVyY2VudGFnZSIpICsKICBnZ3RpdGxlKCJVc2VycycgT3ZlcmFsbCBBdmVyYWdlIEludGVuc2l0eSBNaW51dGVzIENvbnNpc3QgUHJpbWFyaWx5IG9mIFNlZGVudGFyeSBhbmQgTGlnaHRseSBBY3RpdmUgVGltZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJWZXJ5IEFjdGl2ZSIgPSAicmVkIiwgIkZhaXJseSBBY3RpdmUiID0gIm9yYW5nZSIsICJMaWdodGx5IEFjdGl2ZSIgPSAibGlnaHRncmVlbiIsICJTZWRlbnRhcnkiID0gImxpZ2h0Ymx1ZSIpKSArCiBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gTlVMTCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKQoKCmBgYAoKCiJBbmFseXppbmcgZWFjaCBpbmRpdmlkdWFsJ3MgYXZlcmFnZSBjYWxvcmllIGludGFrZSBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZWlyIGluZGl2aWR1YWwgZGlldGFyeSBoYWJpdHMgYW5kIHBhdHRlcm5zLiBCeSBjb21wYXJpbmcgdGhlIGluZGl2aWR1YWwgYXZlcmFnZXMgdG8gdGhlIG92ZXJhbGwgYXZlcmFnZSwgeW91IGNhbiBpZGVudGlmeSBpbmRpdmlkdWFscyB3aG8gY29uc3VtZSBtb3JlIG9yIGZld2VyIGNhbG9yaWVzIGNvbXBhcmVkIHRvIHRoZSBncm91cCBhdmVyYWdlLiBUaGlzIGNvbXBhcmlzb24gY2FuIGhlbHAgaW4gdW5kZXJzdGFuZGluZyB2YXJpYXRpb25zIGluIGNhbG9yaWUgaW50YWtlIGFuZCBwb3RlbnRpYWwgZmFjdG9ycyBpbmZsdWVuY2luZyBpbmRpdmlkdWFsIGRpZmZlcmVuY2VzLiIKCgoKCgoKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9M30KIyBEZWZpbmUgdGhlIGN1c3RvbSBvcmRlciBvZiBsZWdlbmQgaXRlbXMKCmN1c3RvbV9vcmRlciA8LSBjKCJWZXJ5IEFjdGl2ZSIsICJGYWlybHkgQWN0aXZlIiwgIkxpZ2h0bHkgQWN0aXZlIiwgIlNlZGVudGFyeSIpCgoKIyBDcmVhdGUgdGhlIHN0YWNrZWQgaG9yaXpvbnRhbCBiYXIgY2hhcnQKZ2dwbG90KG92ZXJhbGxfYXZlcmFnZV9kZiwgYWVzKHggPSBQZXJjZW50YWdlLCB5ID0gZmFjdG9yKDEpLCBmaWxsID0gZmFjdG9yKHJvdy5uYW1lcyhvdmVyYWxsX2F2ZXJhZ2VfZGYpLCBsZXZlbHMgPSBjdXN0b21fb3JkZXIpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNykgKwogIHhsYWIoIlBlcmNlbnRhZ2UiKSArCiAgeWxhYigiIikgKwogIGdndGl0bGUoIlVzZXJzJyBPdmVyYWxsIEF2ZXJhZ2UgSW50ZW5zaXR5IE1pbnV0ZXMgQ29uc2lzdCBQcmltYXJpbHkgb2YgU2VkZW50YXJ5IGFuZCBMaWdodGx5IEFjdGl2ZSBUaW1lIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgbmFtZSA9ICIiLAogICAgdmFsdWVzID0gYygKICAgICAgIlZlcnkgQWN0aXZlIiA9ICJyZWQiLAogICAgICAiRmFpcmx5IEFjdGl2ZSIgPSAib3JhbmdlIiwKICAgICAgIkxpZ2h0bHkgQWN0aXZlIiA9ICJsaWdodGdyZWVuIiwKICAgICAgIlNlZGVudGFyeSIgPSAibGlnaHRibHVlIgogICAgKSwKICAgIGJyZWFrcyA9IGN1c3RvbV9vcmRlcgogICkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgKyAgIyBSZXZlcnNlIHRoZSBvcmRlciBvZiB0aGUgbGVnZW5kCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksICAjIFJlbW92ZSB0aGUgeS1heGlzIHRleHQKICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIG1hcmdpbiA9IG1hcmdpbihiID0gMjApKSkgKyAjIEFkanVzdCB0aGUgdGl0bGUgc2l6ZSBhbmQgbWFyZ2luCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gOTcsIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDk3LCB5ID0gMSwgbGFiZWwgPSAiICAgOTclIiwgdmp1c3QgPSAtNS41LCBoanVzdCA9IDAuMSkKCgpgYGAKCgpUaGVzZSBpbmRpY2F0b3JzIHByb3ZpZGUgaW5zaWdodHMgaW50byBhY3Rpdml0eSBsZXZlbHMsIHNlZGVudGFyeSBiZWhhdmlvciwgYW5kIGNhbG9yaWUgYnVybi4gVGhleSBjYW4gaGVscCB0cmFjayBwcm9ncmVzcywgc2V0IGdvYWxzLCBhbmQgZXZhbHVhdGUgdXNlciBiZWhhdmlvciBvdmVyIHRpbWUuIFJlbWVtYmVyIHRvIGNvbnNpZGVyIHRoZSBzcGVjaWZpYyBjb250ZXh0IGFuZCBnb2FscyBvZiB5b3VyIGFuYWx5c2lzIHRvIHNlbGVjdCBhbmQgY3VzdG9taXplIHRoZSBtb3N0IHJlbGV2YW50IEtQSXMgZm9yIHlvdXIgdXNlIGNhc2UuIFRoZSBjb250ZXh0IEkgd2lsbCB1c2UgaXMgdGhlIGd1aWRlbGluZXMgZm9yIHBoeXNpY2FsIGFjdGl2aXR5IGFuZCBkaWV0IGZvciBBbWVyaWNhbnM6CgotIFUuUy4gRGVwYXJ0bWVudCBvZiBIZWFsdGggYW5kIEh1bWFuIFNlcnZpY2VzLiAoMjAxOSkuIFBoeXNpY2FsIEFjdGl2aXR5IEd1aWRlbGluZXMgZm9yIEFtZXJpY2FucyAoMm5kIGVkLikuIEF2YWlsYWJsZSBhdCBodHRwczovL2hlYWx0aC5nb3Yvc2l0ZXMvZGVmYXVsdC9maWxlcy8yMDE5LTA5L1BoeXNpY2FsX0FjdGl2aXR5X0d1aWRlbGluZXNfMm5kX2VkaXRpb24ucGRmICAKLSBVLlMuIERlcGFydG1lbnQgb2YgQWdyaWN1bHR1cmUgYW5kIFUuUy4gRGVwYXJ0bWVudCBvZiBIZWFsdGggYW5kIEh1bWFuIFNlcnZpY2VzLiBEaWV0YXJ5IEd1aWRlbGluZXMgZm9yIEFtZXJpY2FucywgMjAyMC0yMDI1LiA5dGggRWRpdGlvbi4gRGVjZW1iZXIgMjAyMC4gQXZhaWxhYmxlIGF0IERpZXRhcnlHdWlkZWxpbmVzLmdvdi8gIAoKCiMjICBFREEgZm9yIGRhaWx5X3NsZWVwX2NsZWFuCgpgYGB7cn0Kc3RyKGRhaWx5X3NsZWVwX2NsZWFuKQpgYGAKLSBhY3Rpdml0eV9kYXRlIChzbGVlcF9kYXkpOiBEYXRlIG9uIHdoaWNoIHRoZSBzbGVlcCBldmVudCBzdGFydGVkLgotIHRvdGFsX3NsZWVwX3JlY29yZHM6IE51bWJlciBvZiByZWNvcmRlZCBzbGVlcCBwZXJpb2RzIGZvciB0aGF0IGRheS4gSW5jbHVkZXMgbmFwcyA+IDYwIG1pbi4KLSB0b3RhbF9taW51dGVzX2FzbGVlcDogVG90YWwgbnVtYmVyIG9mIG1pbnV0ZXMgY2xhc3NpZmllZCBhcyBiZWluZyDigJxhc2xlZXDigJ0uCi0gdG90YWxfdGltZV9pbl9iZWQ6IFRvdGFsIG1pbnV0ZXMgc3BlbnQgaW4gYmVkLCBpbmNsdWRpbmcgYXNsZWVwLCByZXN0bGVzcywgYW5kIGF3YWtlLCB0aGF0IG9jY3VycmVkIGR1cmluZyBhIGRlZmluZWQgc2xlZXAgcmVjb3JkLgoKCgpgYGB7cn0KI1Nhbml0eSBjaGVjazogVmVyaWZ5IHRoYXQgdGhlIHZhbHVlIG9mIHRvdGFsX3RpbWVfaW5fYmVkIGlzIGdyZWF0ZXIgdGhhbiB0b3RhbF9taW51dGVzX2FzbGVlcCwgYXMgd2Ugd291bGQgZXhwZWN0LgpkYWlseV9zbGVlcF9jbGVhbltkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF90aW1lX2luX2JlZCA8IGRhaWx5X3NsZWVwX2NsZWFuJHRvdGFsX21pbnV0ZXNfYXNsZWVwLF0KYGBgCiMjIyBVbml2YXJpYXRlIGFuYWx5c2lzCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQoKbnVtZXJpY2FsX2NvbHMgPC0gZGFpbHlfc2xlZXBfY2xlYW4lPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykKCiMgcGxvdHRpbmcgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMKY29sX25hbWVzIDwtIGNvbG5hbWVzKG51bWVyaWNhbF9jb2xzICkKZm9yIChpIGluIGNvbF9uYW1lcykgewogIHN1cHByZXNzV2FybmluZ3MocHJpbnQoCiAgICBnZ3Bsb3QobnVtZXJpY2FsX2NvbHMgLCBhZXMobnVtZXJpY2FsX2NvbHMgW1tpXV0pKSArCiAgICAgIGdlb21faGlzdG9ncmFtKAogICAgICAgIGJpbnMgPSAzMCwKICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgZmlsbCA9ICJncmF5IiwKICAgICAgICBhZXMoeSA9IC4uZGVuc2l0eS4uKQogICAgICApICsKICAgICAgZ2VvbV9kZW5zaXR5KAogICAgICAgIGNvbG9yID0gImJsdWUiLAogICAgICAgIHNpemUgPSAxCiAgICAgICkgKwogICAgICB4bGFiKGkpICsgeWxhYigiQ291bnQiKSArCiAgICAgIGdndGl0bGUocGFzdGUoIkhpc3RvZ3JhbSB3aXRoIERlbnNpdHkgUGxvdCBvZiIsIGkpKQogICkpCn0KYGBgCgoKIyMjIEJpdmFyaWF0ZSBhbmFseXNpcwoKCiMjIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzCgoKYGBge3IgfQojIENvcnJlbGF0aW9uIGJldHdlZW4gbnVtZXJpY2FsIHZhcmlhYmxlcwpjb3JyIDwtIGNvcihzZWxlY3RfaWYoZGFpbHlfc2xlZXBfY2xlYW4sIGlzLm51bWVyaWMpKQoKZ2djb3JycGxvdChjb3JyLAogICAgICAgICAgIGhjLm9yZGVyID0gVFJVRSwKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICBsYWIgPSBUUlVFLAogICAgICAgICAgIGNvbG9ycyA9IGMoImZpcmVicmljayIsICJ3aGl0ZSIsICJyb3lhbGJsdWUiKSwKICAgICAgICAgICBsYWJfc2l6ZSA9IDQsCiAgICAgICAgICAgbGFiX2NvbCA9ICJibGFjayIsCiAgICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYXRpb24gQmV0d2VlbiBOdW1lcmljYWwgVmFyaWFibGVzIikKCgpgYGAKCiMjIyMgU2NhdHRlcnBsb3RzIG9mIHRvdGFsX21pbnV0ZXNfYXNsZWVwIHZzIHRvdGFsX3RpbWVfaW5fYmVkCgpgYGB7cn0KCmdncGxvdChkYXRhID0gZGFpbHlfc2xlZXBfY2xlYW4sIGFlcyh4ID0gdG90YWxfbWludXRlc19hc2xlZXAsIHkgPSB0b3RhbF90aW1lX2luX2JlZCkpICsKICBnZW9tX3BvaW50KCkKCmBgYAoKIyMjIFVzZXIgQmVoYXZpb3IgZm9yIGRhaWx5IHNsZWVwIGRhdGFzZXQKCgpgYGB7cn0KCiNsaWJyYXJ5KHNjYWxlcykKZnJlcXVlbmN5X3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZGFpbHlfc2xlZXBfY2xlYW4kdG90YWxfc2xlZXBfcmVjb3JkcykpCmZyZXF1ZW5jeV90YWJsZSRQZXJjZW50YWdlIDwtIGZyZXF1ZW5jeV90YWJsZSRGcmVxIC8gc3VtKGZyZXF1ZW5jeV90YWJsZSRGcmVxKSAqIDEwMAoKZ2dwbG90KGRhdGEgPSBmcmVxdWVuY3lfdGFibGUsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShGcmVxLCAiICgiLCBwZXJjZW50KFBlcmNlbnRhZ2UgLyAxMDApLCAiKSIsIHNlcCA9ICIiKSksCiAgICAgICAgICAgIGhqdXN0ID0gMC41LCAgdmp1c3QgPSAtMC40LCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHggPSAiVG90YWwgU2xlZXAgUmVjb3JkcyIsIHkgPSAiRnJlcXVlbmN5IiwKICAgICAgIHRpdGxlID0gIlVuY29tbW9uIE5hcHBpbmc6IDg5JSBvZiBTbGVlcCBSZWNvcmRzIEluZGljYXRlIGEgU2luZ3VsYXIgU2xlZXAgUGVyaW9kLiIsCiAgICAgICBzdWJ0aXRsZSA9ICJJbmNsdWRlcyBuYXBzID4gNjAgbWluLiIpKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIG1hcmdpbiA9IG1hcmdpbihiID0gMjApKSkKCgoKYGBgCgoKIyMjIyBUb3RhbCBNaW51dGVzIEFzbGVlcAoKYGBge3J9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3IgdG90YWxfbWludXRlc19hc2xlZXAKYm94cGxvdChkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9taW51dGVzX2FzbGVlcCwgCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFRvdGFsIE1pbnV0ZXMgQXNsZWVwIiwKICAgICAgICB5bGFiID0gIlRvdGFsIE1pbnV0ZXMgQXNsZWVwIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X3NsZWVwX2NsZWFuJHRvdGFsX21pbnV0ZXNfYXNsZWVwKQpzdGRfZGV2IDwtIHJvdW5kKHNkKGRhaWx5X3NsZWVwX2NsZWFuJHRvdGFsX21pbnV0ZXNfYXNsZWVwKSwgMikKCiMgSWRlbnRpZnkgb3V0bGllcnMKb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9taW51dGVzX2FzbGVlcCkkb3V0CgojIENvdW50IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMKbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKCiMgQ3JlYXRlIHRoZSBsZWdlbmQgbGFiZWwgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46IiwgbWVkaWFuX3ZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCBzdGRfZGV2LCAKICAgICAgICAgICAgICAgICAgICAgICJcbk91dGxpZXJzOiIsIG51bV9vdXRsaWVycykKCiMgQWRkIHRoZSBsZWdlbmQgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbCwgcGNoID0gIiIsIGNvbCA9ICJibGFjayIsIGJ0eSA9ICJuIiwgY2V4ID0gMC44NSkKCmBgYApgYGB7cn0KIyBTbGVlcCBkdXJhdGlvbiBhdmVyYWdlcyBieSBJRHMgd2l0aCBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIGNvdW50IChuKQpzbGVlcF9kZiA8LSBkYWlseV9zbGVlcF9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyA9IG1lYW4odG90YWxfbWludXRlc19hc2xlZXApLAogICAgICAgICAgICBzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyA9IHNkKHRvdGFsX21pbnV0ZXNfYXNsZWVwKSwKICAgICAgICAgICAgbiA9IG4oKSkKCnNsZWVwX2RmCgoKCmBgYAoKYGBge3J9CiMgRHJvcCBJRCAiMjMyMDEyNzAwMiIgZHVlIHRvIGluc3VmZmljaWVudCBkYXRhIGZvciBjb21wdXRpbmcgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLgpzbGVlcF9kZiA8LSBzbGVlcF9kZiAlPiUgCmZpbHRlcihpZCAhPSAiMjMyMDEyNzAwMiIpCmBgYAoKCgpgYGB7cn0Kc2xlZXBfZGYgCmBgYAoKCmBgYHtyfQojIENhbGN1bGF0ZSBwZXJjZW50YWdlcyBmb3IgdGhlIGF2ZXJhZ2UgY29sdW1uCmJlbG93XzZfaG91cnMgPC0gc3VtKHNsZWVwX2RmJGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyA8IDM2MCkgLyBucm93KHNsZWVwX2RmKSAqIDEwMApiZXR3ZWVuXzZfN19ob3VycyA8LSBzdW0oc2xlZXBfZGYkYXZlcmFnZV9zbGVlcF9taW51dGVzID49IDM2MCAmIHNsZWVwX2RmJGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyA8IDQyMCkgLyBucm93KHNsZWVwX2RmKSAqIDEwMAphdF9sZWFzdF83X2hvdXJzIDwtIHN1bShzbGVlcF9kZiRhdmVyYWdlX3NsZWVwX21pbnV0ZXMgPj0gNDIwKSAvIG5yb3coc2xlZXBfZGYpICogMTAwCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBzbGVlcCBkdXJhdGlvbiBjYXRlZ29yaWVzCnBlcmNlbnRhZ2Vfc2xlZXBfZGYgPC0gZGF0YS5mcmFtZSgKICBDYXRlZ29yeSA9IGMoIkJlbG93IDYgaG91cnMiLCAiQmV0d2VlbiA2IGFuZCA3IGhvdXJzIiwgIkF0IGxlYXN0IDcgaG91cnMiKSwKICBQZXJjZW50YWdlX0F2ZXJhZ2UgPSByb3VuZChjKGJlbG93XzZfaG91cnMsIGJldHdlZW5fNl83X2hvdXJzLCBhdF9sZWFzdF83X2hvdXJzKSkKKQoKIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV9zbGVlcF9kZiRDYXRlZ29yeSA8LSBmYWN0b3IocGVyY2VudGFnZV9zbGVlcF9kZiRDYXRlZ29yeSwgbGV2ZWxzID0gYygiQmVsb3cgNiBob3VycyIsICJCZXR3ZWVuIDYgYW5kIDcgaG91cnMiLCAiQXQgbGVhc3QgNyBob3VycyIpKQoKcGVyY2VudGFnZV9zbGVlcF9kZgpgYGAKCmBgYHtyfQoKCmdncGxvdChwZXJjZW50YWdlX3NsZWVwX2RmLCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gUGVyY2VudGFnZV9BdmVyYWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInB1cnBsZSIpICsKICBsYWJzKHggPSAiQXZlcmFnZSBTbGVlcCBEdXJhdGlvbiIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIAogICAgICAgdGl0bGUgPSAiNTIlIG9mIFVzZXJzIEdldCBMZXNzIFRoYW4gNyBIb3VycyBvZiBTbGVlcCBvbiBBdmVyYWdlIERhaWx5IikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAoUGVyY2VudGFnZV9BdmVyYWdlLCAiJSIpKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIpICsgCiAgeWxpbSgwLCAxMDApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKQoKYGBgCgoKCiMjIyMgU2xlZXAgRHVyYXRpb24gQ29uc2lzdGVuY3kKYGBge3J9CiNFcnJvciBiYXJzCgojIENvbnZlcnQgYXZlcmFnZV9zbGVlcF9taW51dGVzIGFuZCBzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyB0byBob3VycwpzbGVlcF9kZiRhdmVyYWdlX3NsZWVwX2hvdXJzIDwtIHNsZWVwX2RmJGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyAvIDYwCnNsZWVwX2RmJHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyA8LSBzbGVlcF9kZiRzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyAvIDYwCgoKIyBDcmVhdGUgYSBiYXIgcGxvdCBmb3IgZWFjaCAnaWQnIHdpdGggZXJyb3IgYmFycyByZXByZXNlbnRpbmcgc3RhbmRhcmQgZGV2aWF0aW9uCmdncGxvdChzbGVlcF9kZiwgYWVzKHggPSBpZCwgeSA9IGF2ZXJhZ2Vfc2xlZXBfaG91cnMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBhdmVyYWdlX3NsZWVwX2hvdXJzIC0gc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX21pbnV0ZXMgLyA2MCwKICAgICAgICAgICAgICAgICAgICB5bWF4ID0gYXZlcmFnZV9zbGVlcF9ob3VycyArIHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9taW51dGVzIC8gNjApLAogICAgICAgICAgICAgICAgd2lkdGggPSAwLjIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIklEIiwgeSA9ICJBdmVyYWdlIFNsZWVwIER1cmF0aW9uIChob3VycykiLAogICAgICAgdGl0bGUgPSAiU2xlZXAgQ29uc2lzdGVuY3k6IEF2ZXJhZ2UgU2xlZXAgRHVyYXRpb24gd2l0aCBFcnJvciBCYXJzIiwKICAgICAgIHN1YnRpdGxlID0gIkVycm9yIGJhcnMgcmVwcmVzZW50IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYXJvdW5kIHRoZSBtZWFuLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA3LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMiwgMSkpICAjIEFkanVzdCB0aGUgcmFuZ2UgYXMgbmVlZGVkCgpgYGAKCgoKCmBgYHtyfQojIENhbGN1bGF0ZSBzbGVlcCBkdXJhdGlvbiBhdmVyYWdlcyBhbmQgc3RhbmRhcmQgZGV2aWF0aW9ucyBpbiBob3VycwpzbGVlcF9kZiA8LSBkYWlseV9zbGVlcF9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCksCiAgICAgICAgICAgIGF2ZXJhZ2Vfc2xlZXBfaG91cnMgPSBtZWFuKHRvdGFsX21pbnV0ZXNfYXNsZWVwKSAvIDYwLCAgICAgICAjIENvbnZlcnQgbWludXRlcyB0byBob3VycwogICAgICAgICAgICBhdmVyYWdlX3RpbWVfaW5fYmVkX2hvdXJzID0gbWVhbih0b3RhbF90aW1lX2luX2JlZCkgLyA2MCwgICAKICAgICAgICAgICAgc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzID0gc2QodG90YWxfbWludXRlc19hc2xlZXApIC8gNjAsICAgICAKICAgICAgICAgICAgc3RhbmRhcmRfZGV2aWF0aW9uX3RpbWVfaW5fYmVkX2hvdXJzID0gc2QodG90YWxfdGltZV9pbl9iZWQpIC8gNjAsICAgIAogICAgICAgICAgICkgJT4lCiAgbXV0YXRlKHRpbWVfZGlmZmVyZW5jZV9ob3VycyA9IGF2ZXJhZ2VfdGltZV9pbl9iZWRfaG91cnMgLSBhdmVyYWdlX3NsZWVwX2hvdXJzLCAgIyBDYWxjdWxhdGUgdGhlIHRpbWUgZGlmZmVyZW5jZSBpbiBob3VycwogICAgICAgICBhdmVyYWdlX2F3YWtlX2luX2JlZF9ob3VycyA9IHRpbWVfZGlmZmVyZW5jZV9ob3VycywgICMgUmVuYW1lIGNvbHVtbiAiYXdha2VfaW5fYmVkIgogICAgICAgICBzZF9hd2FrZV9pbl9iZWRfaG91cnMgPSBzZCh0aW1lX2RpZmZlcmVuY2VfaG91cnMpKSAgIyBDYWxjdWxhdGUgU0QgZm9yICJhd2FrZV9pbl9iZWQiIGluIGhvdXJzCgoKc2xlZXBfZGYKCgoKYGBgCgoKYGBge3J9CiMgRHJvcCBJRCAiMjMyMDEyNzAwMiIgZHVlIHRvIGluc3VmZmljaWVudCBkYXRhIGZvciBjb21wdXRpbmcgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLgpzbGVlcF9kZiA8LSBzbGVlcF9kZiAlPiUgCmZpbHRlcihpZCAhPSAiMjMyMDEyNzAwMiIpCmRpbShzbGVlcF9kZikKYGBgCgoKCiNuZXh0OiB1bmRlcnN0YW5kIHRoaXMgZnVuY3Rpb24gCgoKCgoKYGBge3J9CmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0IDwtIGZ1bmN0aW9uKGRhdGFfZnJhbWUsIGNvbHVtbnNfdG9fYW5hbHl6ZSwgZGVjaW1hbF9wbGFjZXMgPSAyKSB7CiAgbnVtX2NvbHVtbnMgPC0gbGVuZ3RoKGNvbHVtbnNfdG9fYW5hbHl6ZSkKICBudW1fcm93cyA8LSBjZWlsaW5nKG51bV9jb2x1bW5zIC8gMikKICAKICBwYXIobWZyb3cgPSBjKG51bV9yb3dzLCAyKSkgICMgU2V0IHRoZSBwbG90dGluZyBsYXlvdXQKICAKICBmb3IgKGkgaW4gMTpudW1fY29sdW1ucykgewogICAgY29sdW1uX25hbWUgPC0gY29sdW1uc190b19hbmFseXplW2ldCiAgICBib3hwbG90KGRhdGFfZnJhbWVbW2NvbHVtbl9uYW1lXV0sCiAgICAgICAgICAgIHlsYWIgPSBjb2x1bW5fbmFtZSkKICAgIAogICAgbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihkYXRhX2ZyYW1lW1tjb2x1bW5fbmFtZV1dKQogICAgc3RkX2RldiA8LSByb3VuZChzZChkYXRhX2ZyYW1lW1tjb2x1bW5fbmFtZV1dKSwgZGVjaW1hbF9wbGFjZXMpCiAgICBvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhdGFfZnJhbWVbW2NvbHVtbl9uYW1lXV0pJG91dAogICAgbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKICAgIAogICAgbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46Iiwgcm91bmQobWVkaWFuX3ZhbHVlLCBkZWNpbWFsX3BsYWNlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuU0Q6Iiwgc3RkX2RldiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCiAgICAKICAgIGxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIsIGNleCA9IDAuNzUpCiAgfQogIAogIHBhcihtZnJvdyA9IGMoMSwgMSkpICAjIFJlc2V0IHRoZSBwbG90dGluZyBsYXlvdXQgdG8gZGVmYXVsdAp9CgojIENvbHVtbnMgdG8gYW5hbHl6ZQpjb2x1bW5zX3RvX2FuYWx5emUgPC0gYygiYXZlcmFnZV9zbGVlcF9ob3VycyIsICJhdmVyYWdlX2F3YWtlX2luX2JlZF9ob3VycyIpCgojIENhbGwgdGhlIGZ1bmN0aW9uIHRvIGNyZWF0ZSBib3hwbG90cyBpbiBvbmUgb3V0cHV0CmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0KHNsZWVwX2RmLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikKCmBgYAoKCmBgYHtyfQojIENvbHVtbnMgdG8gYW5hbHl6ZQpjb2x1bW5zX3RvX2FuYWx5emUgPC0gYygic3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzIiwgInNkX2F3YWtlX2luX2JlZF9ob3VycyIpCgojIENhbGwgdGhlIGZ1bmN0aW9uCmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0KHNsZWVwX2RmLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikKYGBgCgoKYGBge3J9CiNDb2x1bW5zIHdpdGggb3V0bGllcnMgdG8gcmVtb3ZlCmNvbHVtbnNfd2l0aF9vdXRsaWVycyA8LSBjKCJhdmVyYWdlX3NsZWVwX2hvdXJzIiwgImF2ZXJhZ2VfYXdha2VfaW5fYmVkX2hvdXJzIiwgInN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyIpCgojIEZ1bmN0aW9uIHRvIHJlbW92ZSBvdXRsaWVycyBmcm9tIGEgY29sdW1uCnJlbW92ZV9vdXRsaWVycyA8LSBmdW5jdGlvbihkYXRhLCBjb2x1bW5fbmFtZSkgewogIG91dGxpZXJfYm91bmRzIDwtIGJveHBsb3Quc3RhdHMoZGF0YVtbY29sdW1uX25hbWVdXSkkb3V0CiAgZGF0YV9ub19vdXRsaWVyczwtIGRhdGFbIShkYXRhW1tjb2x1bW5fbmFtZV1dICVpbiUgb3V0bGllcl9ib3VuZHMpLCBdCiAgcmV0dXJuKGRhdGFfbm9fb3V0bGllcnMpCn0KCiMgTG9vcCB0aHJvdWdoIGVhY2ggY29sdW1uIGFuZCByZW1vdmUgb3V0bGllcnMKZm9yIChjb2wgaW4gY29sdW1uc193aXRoX291dGxpZXJzKSB7CiAgc2xlZXBfZGYgPC0gcmVtb3ZlX291dGxpZXJzKHNsZWVwX2RmLCBjb2wpCn0KCmBgYAoKCmBgYHtyfQpzbGVlcF9kZgpgYGAKCgoKYGBge3J9CiMgQ2hlY2sgaWYgb3V0bGllcnMgd2VyZSByZW1vdmVkCmNvbHVtbnNfdG9fYW5hbHl6ZSA8LSBjKCJhdmVyYWdlX3NsZWVwX2hvdXJzIiwgImF2ZXJhZ2VfYXdha2VfaW5fYmVkX2hvdXJzIiwgInN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyIpCgoKIyBDYWxsIHRoZSBmdW5jdGlvbiB0byBjcmVhdGUgYm94cGxvdHMgaW4gb25lIG91dHB1dApjcmVhdGVfYm94cGxvdHNfaW5fb25lX291dHB1dChzbGVlcF9kZiwgY29sdW1uc190b19hbmFseXplLCBkZWNpbWFsX3BsYWNlcyA9IDIpCmBgYAoKCmBgYHtyfQojTGV0IHVzIGRpdmlkZSB0aGUgdXNlcnMgaW50byBpcnJlZ3VsYXIgc2xlZXBlcnMgYW5kIHJlZ3VsYXIgc2xlZXBlcnMuIFdlIHdpbGwgdXNlIHRoZSA3NXRoIHBlcmNlbnRpbGUgYXMgdGhlIHRocmVzaG9sZCB0byBkZXRlcm1pbmUgaXJyZWd1bGFyIHNsZWVwZXJzLiBUaGUgcmVzdCB3aWxsIGJlIGNvbnNpZGVyZWQgcmVndWxhciBzbGVlcGVycy4KCiMgRGVmaW5lIHRoZSBUaHJlc2hvbGQgKGUuZy4sIHVzaW5nIHRoZSA3NXRoIHBlcmNlbnRpbGUpCnRocmVzaG9sZCA8LSBxdWFudGlsZShzbGVlcF9kZiRzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfaG91cnMsIDAuNzUpCgojIENyZWF0ZSBhIG5ldyBjb2x1bW4gInNsZWVwZXJfdHlwZSIgYmFzZWQgb24gdGhlIHRocmVzaG9sZApzbGVlcF9kZiRzbGVlcGVyX3R5cGUgPC0gaWZlbHNlKHNsZWVwX2RmJHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyA+IHRocmVzaG9sZCwgImlycmVndWxhciIsICJyZWd1bGFyIikKCgpgYGAKCgoKCmBgYHtyfQpzbGVlcF9kZgpgYGAKCgoKYGBge3J9CiMgc2xlZXBfdHlwZSBjb3VudHMKdGFibGUoc2xlZXBfZGYkc2xlZXBlcl90eXBlKQoKYGBgCgpgYGB7cn0Kc2xlZXBfZGYKYGBgCgoKCgoKYGBge3J9Cgpjb2xvcl9vcHRpb25zIDwtIGMoIiNFNjlGMDAiLCAiIzAwNzJCMiIpICMgQmx1ZTogIiMwMDcyQjIiLCBPcmFuZ2U6ICIjRTY5RjAwIgoKIyBGdW5jdGlvbiB0byBjcmVhdGUgdGhlIHZpb2xpbiBwbG90IGZvciBhIGdpdmVuIHktYXhpcyBjb2x1bW4KY3JlYXRlX3Zpb2xpbl9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIHhfYXhpc19jb2wsIHlfYXhpc19jb2wpIHsKICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0geF9heGlzX2NvbCwgeSA9IHlfYXhpc19jb2wsIGZpbGwgPSB4X2F4aXNfY29sKSkgKwogICAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCBkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwgdHJpbSA9IEZBTFNFKSArCiAgICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpICsKICAgIGxhYnMoeCA9ICJTbGVlcGVyIFR5cGUiLCB5ID0geV9heGlzX2NvbCwgdGl0bGUgPSBwYXN0ZSgiQ29tcGFyaXNvbiIseF9heGlzX2NvbCwiZm9yIiwgeV9heGlzX2NvbCkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX29wdGlvbnMpICsKICAgIHRoZW1lX21pbmltYWwoKQp9CgoKYGBgCgoKYGBge3J9CiMgQ2FsbCB0aGUgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSB2aW9saW4gcGxvdHMgZm9yIGVhY2ggY29sdW1uCmZvciAoY29sIGluIGMoImF2ZXJhZ2Vfc2xlZXBfaG91cnMiLCAic3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzIiwgImF2ZXJhZ2VfYXdha2VfaW5fYmVkX2hvdXJzIiwic2RfYXdha2VfaW5fYmVkX2hvdXJzIikpIHsKICBwbG90IDwtIGNyZWF0ZV92aW9saW5fcGxvdChzbGVlcF9kZiwgInNsZWVwZXJfdHlwZSIsIGNvbCkKICBwcmludChwbG90KQp9CgpgYGAKT2JzZXJ2YXRpb25zOgoKLSBSZWd1bGFyIHNsZWVwZXJzIHRlbmQgdG8gaGF2ZSBoaWdoZXIgbWVkaWFuIGF2ZXJhZ2Ugc2xlZXAgaG91cnMgY29tcGFyZWQgdG8gaXJyZWd1bGFyIHNsZWVwZXJzLlRoaXMgc3VnZ2VzdHMgdGhhdCBpbmRpdmlkdWFscyBjbGFzc2lmaWVkIGFzIHJlZ3VsYXIgc2xlZXBlcnMgYXJlIGxpa2VseSBnZXR0aW5nIG1vcmUgc2xlZXAgb24gYXZlcmFnZSB0aGFuIHRob3NlIGNhdGVnb3JpemVkIGFzIGlycmVndWxhciBzbGVlcGVycy4KCi0gQWRkaXRpb25hbGx5LCB0aGUgc3ByZWFkIG9mIHRoZSAiYXZlcmFnZV9zbGVlcF9ob3VycyIgZm9yIGlycmVndWxhciBzbGVlcGVycyBhcHBlYXJzIHRvIGJlIHdpZGVyLCBpbmRpY2F0aW5nIG1vcmUgdmFyaWFiaWxpdHkgaW4gdGhlaXIgc2xlZXAgZHVyYXRpb24uIEluIGNvbnRyYXN0LCB0aGUgdmlvbGluIHBsb3QgZm9yIHJlZ3VsYXIgc2xlZXBlcnMgc2hvd3MgYSBuYXJyb3dlciBzcHJlYWQsIHN1Z2dlc3RpbmcgdGhhdCB0aGVpciBzbGVlcCBkdXJhdGlvbiBpcyBtb3JlIGNvbnNpc3RlbnQuCgotIFJlZ3VsYXIgc2xlZXBlcnMgZXhoaWJpdCBhIHNsaWdodGx5IGhpZ2hlciBtZWRpYW4gYXZlcmFnZSBhd2FrZS1pbi1iZWQgZHVyYXRpb24gY29tcGFyZWQgdG8gaXJyZWd1bGFyIHNsZWVwZXJzLgoKU3VtbWFyeTogUmVndWxhciBzbGVlcGVycyBnZXQgbW9yZSBzbGVlcCBvbiBhdmVyYWdlLCBoYXZlIGEgbW9yZSBjb25zaXN0ZW50IHNsZWVwIGR1cmF0aW9uLCBhbmQgc2xpZ2h0bHkgaGlnaGVyIG1lZGlhbiBhd2FrZS1pbi1iZWQgZHVyYXRpb24gdGhhbiBpcnJlZ3VsYXIgc2xlZXBlcnMuCgoKCgoKCgoKLSBUaGUgbWVhbiBvZiAnc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzJyBjYW4gcHJvdmlkZSB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSBzbGVlcCBjb25zaXN0ZW5jeSBvZiB0aGUgdXNlcnMgaW4gdGhlIGRhdGFzZXQuIEhlcmUncyB3aGF0IHRoZSBtZWFuIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gY2FuIHRlbGwgeW91IGFib3V0IHRoZSB1c2VyczoKCkF2ZXJhZ2UgU2xlZXAgVmFyaWFiaWxpdHk6IFRoZSBtZWFuIG9mICdzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfaG91cnMnIGdpdmVzIHlvdSBhbiBhdmVyYWdlIG1lYXN1cmUgb2YgaG93IG11Y2ggdGhlIHVzZXJzJyBzbGVlcCBkdXJhdGlvbnMgdmFyeSBmcm9tIHRoZWlyIHJlc3BlY3RpdmUgYXZlcmFnZSBzbGVlcCBkdXJhdGlvbi4gQSBoaWdoZXIgbWVhbiBzdGFuZGFyZCBkZXZpYXRpb24gaW1wbGllcyBtb3JlIGluY29uc2lzdGVuY3kgaW4gc2xlZXAgcGF0dGVybnMgYW1vbmcgdXNlcnMuCgpTbGVlcCBTY2hlZHVsZSBTdGFiaWxpdHk6IFVzZXJzIHdpdGggYSBoaWdoZXIgbWVhbiBzdGFuZGFyZCBkZXZpYXRpb24gbWF5IGhhdmUgaXJyZWd1bGFyIHNsZWVwIHNjaGVkdWxlcywgd2l0aCB2YXJ5aW5nIHNsZWVwIGR1cmF0aW9ucyBvbiBkaWZmZXJlbnQgZGF5cywgaW5kaWNhdGluZyBwb3RlbnRpYWwgaXNzdWVzIHdpdGggc2xlZXAgY29uc2lzdGVuY3kuCgpTbGVlcCBRdWFsaXR5OiBJbiBzb21lIGNhc2VzLCBhIGhpZ2hlciBtZWFuIHN0YW5kYXJkIGRldmlhdGlvbiBtaWdodCBpbmRpY2F0ZSBwb29yIHNsZWVwIHF1YWxpdHksIGFzIHVuc3RhYmxlIG9yIGZyYWdtZW50ZWQgc2xlZXAgcGF0dGVybnMgY2FuIGxlYWQgdG8gbGVzcyByZXN0b3JhdGl2ZSBzbGVlcC4KClNsZWVwIFJlZ3VsYXJpdHk6IEEgbG93ZXIgbWVhbiBzdGFuZGFyZCBkZXZpYXRpb24gZ2VuZXJhbGx5IHN1Z2dlc3RzIHRoYXQgdXNlcnMgaGF2ZSBtb3JlIGNvbnNpc3RlbnQgc2xlZXAgcGF0dGVybnMgYW5kIGFkaGVyZSB0byBhIG1vcmUgcmVndWxhciBzbGVlcCBzY2hlZHVsZS4KCkhlYWx0aCBJbmRpY2F0b3JzOiBTbGVlcCBjb25zaXN0ZW5jeSBjYW4gYmUgbGlua2VkIHRvIG92ZXJhbGwgaGVhbHRoIGFuZCB3ZWxsLWJlaW5nLiBVc2VycyB3aXRoIGEgbG93ZXIgbWVhbiBzdGFuZGFyZCBkZXZpYXRpb24gbWlnaHQgZXhwZXJpZW5jZSBiZXR0ZXIgaGVhbHRoIG91dGNvbWVzLCBhcyBjb25zaXN0ZW50IHNsZWVwIGlzIGFzc29jaWF0ZWQgd2l0aCBpbXByb3ZlZCBwaHlzaWNhbCBhbmQgbWVudGFsIGhlYWx0aC4KClBvdGVudGlhbCBTbGVlcCBEaXNvcmRlcnM6IFVzZXJzIHdpdGggaGlnaCBzdGFuZGFyZCBkZXZpYXRpb24gdmFsdWVzIG1heSBoYXZlIGVycmF0aWMgc2xlZXAgcGF0dGVybnMsIHdoaWNoIGNvdWxkIGJlIGluZGljYXRpdmUgb2Ygc2xlZXAgZGlzb3JkZXJzIG9yIGRpc3R1cmJhbmNlcyB0aGF0IHJlcXVpcmUgZnVydGhlciBpbnZlc3RpZ2F0aW9uLgoKU2xlZXAgSGFiaXRzOiBVbmRlcnN0YW5kaW5nIHRoZSBtZWFuIHN0YW5kYXJkIGRldmlhdGlvbiBjYW4gb2ZmZXIgaW5zaWdodHMgaW50byBzbGVlcCBoYWJpdHMgYW5kIGJlaGF2aW9ycy4gRm9yIGV4YW1wbGUsIHVzZXJzIHdpdGggaGlnaCB2YXJpYWJpbGl0eSBtaWdodCBoYXZlIGlycmVndWxhciBiZWR0aW1lIHJvdXRpbmVzIG9yIGZyZXF1ZW50IGRpc3J1cHRpb25zIGR1cmluZyBzbGVlcC4KClN0cmVzcyBMZXZlbHM6IEluY29uc2lzdGVudCBzbGVlcCBwYXR0ZXJucyBjYW4gYmUgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBzdHJlc3MgbGV2ZWxzLCBzbyBhIGhpZ2hlciBtZWFuIHN0YW5kYXJkIGRldmlhdGlvbiBtaWdodCBoaW50IGF0IGluY3JlYXNlZCBzdHJlc3Mgb3IgYW54aWV0eSBsZXZlbHMgYW1vbmcgc29tZSB1c2Vycy4KCgoKCgoKCgoKCgoKCgoKYGBge3J9CgoKIyAjIEFzc3VtaW5nIHNsZWVwX2RmIGNvbnRhaW5zICdpZCcsICdhdmVyYWdlX3NsZWVwX21pbnV0ZXMnLCBhbmQgJ3N0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9taW51dGVzJwojIAojICMgQ2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzCiMgc2xlZXBfZGYgPC0gdHJhbnNmb3JtKHNsZWVwX2RmLCAKIyAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJfY2kgPSBhdmVyYWdlX3NsZWVwX21pbnV0ZXMgLSAxLjk2ICogc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX21pbnV0ZXMgLyBzcXJ0KG4pLAojICAgICAgICAgICAgICAgICAgICAgICB1cHBlcl9jaSA9IGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyArIDEuOTYgKiBzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyAvIHNxcnQobikpCiMgCiMgIyBQbG90IHRoZSBiYXIgcGxvdCB3aXRoIGNvbmZpZGVuY2UgaW50ZXJ2YWxzCiMgZ2dwbG90KHNsZWVwX2RmLCBhZXMoeCA9IGlkLCB5ID0gYXZlcmFnZV9zbGVlcF9taW51dGVzKSkgKwojICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwojICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGxvd2VyX2NpLCB5bWF4ID0gdXBwZXJfY2kpLAojICAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpLCBjb2xvciA9ICJibGFjayIpICsKIyAgIGxhYnMoeCA9ICJJRCIsIHkgPSAiQXZlcmFnZSBTbGVlcCBEdXJhdGlvbiAobWludXRlcykiLAojICAgICAgICB0aXRsZSA9ICJTbGVlcCBDb25zaXN0ZW5jeTogQXZlcmFnZSBTbGVlcCBEdXJhdGlvbiB3aXRoIENvbmZpZGVuY2UgSW50ZXJ2YWxzIiwKIyAgICAgICAgc3VidGl0bGUgPSAiRXJyb3IgYmFycyByZXByZXNlbnQgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFscyBhcm91bmQgdGhlIG1lYW4uIikgKwojICAgdGhlbWVfbWluaW1hbCgpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCgoKYGBgCgoKCgoKCgoKCgpuZXh0OgoKIyBJIG5lZWQgdG8gY3JlYXRlIGNhdGVnb3JpZXMgZm9yIHZhcmlhYmlsaXR5CkNyZWF0ZSBhIHNsZWVwIGNvbnNpc3RlbmN5IHNjb3JlIHRoYXQgY29tYmluZXMgZmFjdG9ycyBsaWtlIHJlZ3VsYXJpdHkgb2Ygc2xlZXAgZHVyYXRpb24gYW5kIGJlZHRpbWUgdG8gZ2V0IGFuIG92ZXJhbGwgbWVhc3VyZSBvZiBzbGVlcCBwYXR0ZXJuIGNvbnNpc3RlbmN5LgoKIFN0ZXAgNTogQ3JlYXRlIGEgc2xlZXAgY29uc2lzdGVuY3kgc2NvcmUKIFlvdSBjYW4gZGVmaW5lIHlvdXIgb3duIGZvcm11bGEgYmFzZWQgb24gdmFyaW91cyBmYWN0b3JzIGxpa2UgcmVndWxhcml0eSBvZiBzbGVlcCBkdXJhdGlvbiwgYmVkdGltZSwgZXRjLgpGb3IgZXhhbXBsZSwgeW91IGNvdWxkIHVzZSBhIHdlaWdodGVkIGF2ZXJhZ2Ugb2Ygc3RhbmRhcmQgZGV2aWF0aW9uLCBjb3JyZWxhdGlvbiwgYW5kIG90aGVyIG1ldHJpY3MuCgoKZG8gc2FtZSBhcyBJIGRpZCBmb3Igc3RlcHMgZm9yIG1pbnV0ZXMgYXNfc2xlZXAKCgpBZHVsdAkxOC02MCB5ZWFycwk3IG9yIG1vcmUgaG91cnMgcGVyIG5pZ2h0CiBjaGVjayBmb3Igc2xlZW8gY29uc2lzdGVuY3kKQmUgY29uc2lzdGVudC4gR28gdG8gYmVkIGF0IHRoZSBzYW1lIHRpbWUgZWFjaCBuaWdodCBhbmQgZ2V0IHVwIGF0IHRoZSBzYW1lIHRpbWUgZWFjaCBtb3JuaW5nLCBpbmNsdWRpbmcgb24gdGhlIHdlZWtlbmRzLgoKCgoKVG8gYW5hbHl6ZSBzbGVlcCB1c2VyIGJlaGF2aW9yIGluIHRoZSBwcm92aWRlZCBkYXRhc2V0LCB5b3UgY2FuIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcga2V5IHBlcmZvcm1hbmNlIGluZGljYXRvcnMgKEtQSXMpOgoKMS4gU2xlZXAgRWZmaWNpZW5jeTogQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHRpbWUgdXNlcnMgc3BlbmQgYXNsZWVwIGNvbXBhcmVkIHRvIHRoZSB0b3RhbCB0aW1lIGluIGJlZC4gVGhpcyBjYW4gYmUgY29tcHV0ZWQgYXMgYCh0b3RhbF9zbGVlcF9yZWNvcmRzX2FzbGVlcCAvIHRvdGFsX3RpbWVfaW5fYmVkKSAqIDEwMGAuIEhpZ2hlciBzbGVlcCBlZmZpY2llbmN5IGluZGljYXRlcyBtb3JlIGNvbnNvbGlkYXRlZCBhbmQgcmVzdGZ1bCBzbGVlcC4KCjIuIEF2ZXJhZ2UgU2xlZXAgRHVyYXRpb246IENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBkdXJhdGlvbiBvZiBzbGVlcCBmb3IgZWFjaCB1c2VyIGJ5IGRpdmlkaW5nIHRoZSB0b3RhbCBzbGVlcCByZWNvcmRzIGFzbGVlcCBieSB0aGUgdG90YWwgbnVtYmVyIG9mIHNsZWVwIHJlY29yZHMuIFRoaXMgcHJvdmlkZXMgYW4gaW5kaWNhdGlvbiBvZiB0aGUgdHlwaWNhbCBsZW5ndGggb2Ygc2xlZXAgc2Vzc2lvbnMuCgozLiBTbGVlcCBRdWFsaXR5OiBBc3Nlc3Mgc2xlZXAgcXVhbGl0eSBieSBjb25zaWRlcmluZyBmYWN0b3JzIHN1Y2ggYXMgd2FrZWZ1bG5lc3MgZHVyaW5nIHRoZSBuaWdodCwgc2xlZXAgZGlzdHVyYmFuY2VzLCBvciB0aGUgbnVtYmVyIG9mIGF3YWtlbmluZ3MuIFlvdSBjYW4gZXZhbHVhdGUgc2xlZXAgcXVhbGl0eSBieSBleGFtaW5pbmcgcGF0dGVybnMgaW4gdGhlIHNsZWVwIHJlY29yZHMsIGlkZW50aWZ5aW5nIGRpc3J1cHRpb25zLCBvciB1c2luZyBzdWJqZWN0aXZlIHJhdGluZ3MgaWYgYXZhaWxhYmxlLgoKNC4gU2xlZXAgTGF0ZW5jeTogTWVhc3VyZSB0aGUgdGltZSBpdCB0YWtlcyBmb3IgdXNlcnMgdG8gZmFsbCBhc2xlZXAgYWZ0ZXIgZ2V0dGluZyBpbnRvIGJlZC4gVGhpcyBjYW4gYmUgY2FsY3VsYXRlZCBhcyB0aGUgYXZlcmFnZSB0aW1lIGZyb20gZ29pbmcgdG8gYmVkIHRvIHRoZSBvbnNldCBvZiBzbGVlcC4KCjUuIFNsZWVwIENvbnNpc3RlbmN5OiBBbmFseXplIHRoZSBjb25zaXN0ZW5jeSBvZiBzbGVlcCBwYXR0ZXJucyBieSBleGFtaW5pbmcgdGhlIHJlZ3VsYXJpdHkgb2Ygc2xlZXAgZHVyYXRpb24gYW5kIGJlZHRpbWUuIElycmVndWxhciBzbGVlcCBwYXR0ZXJucyBtYXkgaW5kaWNhdGUgZGlzcnVwdGVkIG9yIGluY29uc2lzdGVudCBzbGVlcCByb3V0aW5lcy4KCjYuIFNsZWVwIEFyY2hpdGVjdHVyZTogQXNzZXNzIHRoZSBkaXN0cmlidXRpb24gb2Ygc2xlZXAgc3RhZ2VzLCBzdWNoIGFzIGRlZXAgc2xlZXAsIGxpZ2h0IHNsZWVwLCBhbmQgUkVNIHNsZWVwLiBUaGlzIGFuYWx5c2lzIHJlcXVpcmVzIGFkZGl0aW9uYWwgc2xlZXAgc3RhZ2UgZGF0YSBiZXlvbmQgdGhlIHZhcmlhYmxlcyBwcm92aWRlZCBpbiB0aGUgZGF0YXNldC4KCjcuIFNsZWVwIFZhcmlhYmlsaXR5OiBBbmFseXplIHRoZSB2YXJpYXRpb24gaW4gc2xlZXAgZHVyYXRpb24gYW5kIGJlZHRpbWUgYWNyb3NzIGRpZmZlcmVudCBkYXlzIG9yIHdlZWtzLiBIaWdoZXIgdmFyaWFiaWxpdHkgbWF5IGluZGljYXRlIGlycmVndWxhciBzbGVlcCBwYXR0ZXJucyBvciBkaXNydXB0ZWQgc2xlZXAgc2NoZWR1bGVzLgoKVGhlc2UgS1BJcyBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHNsZWVwIGJlaGF2aW9yIGFuZCBwYXR0ZXJucywgaGVscGluZyB0byBpZGVudGlmeSBhcmVhcyBmb3IgaW1wcm92ZW1lbnQgb3IgcG90ZW50aWFsIHNsZWVwIGlzc3Vlcy4gUmVtZW1iZXIgdG8gY29uc2lkZXIgdGhlIGxpbWl0YXRpb25zIG9mIHRoZSBkYXRhc2V0IGFuZCB0aGUgc3BlY2lmaWMgY29udGV4dCBvZiB0aGUgc2xlZXAgZGF0YSBiZWluZyBhbmFseXplZC4KCgojIyAgRURBIGZvciBob3VybHlfYWN0aXZpdHlfY2xlYW4KCgojIyAgRURBIGZvciBzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbgoKIyMgIEVEQSBmb3Igd2VpZ2h0X2xvZ3NfY2xlYW4KLSBXZWlnaHQgUmVwb3J0aW5nIEJlaGF2aW9yOiBBc3Nlc3MgdGhlIHJlcG9ydGluZyBiZWhhdmlvciBvZiB1c2VycyB1c2luZyB0aGUgSXNNYW51YWxSZXBvcnQgdmFyaWFibGUuIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBtYW51YWwgd2VpZ2h0IHJlcG9ydHMgY29tcGFyZWQgdG8gdG90YWwgd2VpZ2h0IHJlcG9ydHMgdG8gZGV0ZXJtaW5lIGhvdyBhY3RpdmVseSB1c2VycyBhcmUgcHJvdmlkaW5nIHdlaWdodCB1cGRhdGVzLiBUaGlzIGNhbiBpbmRpY2F0ZSB1c2VyIGVuZ2FnZW1lbnQgYW5kIG1vdGl2YXRpb24gdG8gdHJhY2sgdGhlaXIgd2VpZ2h0LgoKLSBEYXRhIENvbXBsZXRlbmVzczogQW5hbHl6ZSB0aGUgY29tcGxldGVuZXNzIGFuZCBxdWFsaXR5IG9mIGRhdGEgdXNpbmcgdmFyaWFibGVzIHN1Y2ggYXMgTG9nSWQuIEFzc2VzcyBpZiB0aGVyZSBhcmUgbWlzc2luZyBvciBlcnJvbmVvdXMgZGF0YSBwb2ludHMgdGhhdCBtYXkgaW1wYWN0IHRoZSBhbmFseXNpcy4gRXhjbHVkZSBvciBoYW5kbGUgc3VjaCBkYXRhIHBvaW50cyBhcHByb3ByaWF0ZWx5IHRvIGVuc3VyZSBhY2N1cmF0ZSBpbnNpZ2h0cy4KCi0gQk1JIGRpc3RyaWJ1dGlvbiAocGVyY2VudGFnZSBhYm92ZSBub3JtYWwgQk1JKQoKLSBXZWlnaHQgVHJlbmRzOiBBbmFseXplIHRoZSB0cmVuZHMgaW4gd2VpZ2h0IG92ZXIgdGltZSAoRGF0ZSkgdG8gdW5kZXJzdGFuZCBpZiB1c2VycyBhcmUgZXhwZXJpZW5jaW5nIHdlaWdodCBsb3NzLCBnYWluLCBvciBzdGFiaWxpdHkuIFBsb3R0aW5nIHdlaWdodCAoV2VpZ2h0S2cgb3IgV2VpZ2h0UG91bmRzKSBhZ2FpbnN0IHRpbWUgY2FuIHJldmVhbCBwYXR0ZXJucywgZmx1Y3R1YXRpb25zLCBvciBzaWduaWZpY2FudCBjaGFuZ2VzIGluIHdlaWdodC4gWW91IGNhbiBhbHNvIGNhbGN1bGF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIHN1Y2ggYXMgYXZlcmFnZSB3ZWlnaHQsIHN0YW5kYXJkIGRldmlhdGlvbiwgb3IgcmF0ZSBvZiB3ZWlnaHQgY2hhbmdlIHRvIGdhaW4gaW5zaWdodHMgaW50byB1c2VyIGJlaGF2aW9yLgoKCgojIEluc2lnaHRzIGFuZCByZWNvbW1lbmRhdGlvbnMKCmh0dHBzOi8vd3d3LmNkYy5nb3YvbW13ci92b2x1bWVzLzY4L3dyL21tNjgyM2ExLmh0bQoKZmlsZTovLy9Vc2Vycy92aXZpYW5iYXJyb3MvRGVza3RvcC9QaHlzaWNhbF9BY3Rpdml0eV9HdWlkZWxpbmVzXzJuZF9lZGl0aW9uLnBkZgoKCgojIEFwcGVuZGl4IAoKCgojCmh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEzMDM1ODM0L3Bsb3QtZXZlcnktY29sdW1uLWluLWEtZGF0YS1mcmFtZS1hcy1hLWhpc3RvZ3JhbS1vbi1vbmUtcGFnZS11c2luZy1nZ3Bsb3QgCgoKCgpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMzAzNTgzNC9wbG90LWV2ZXJ5LWNvbHVtbi1pbi1hLWRhdGEtZnJhbWUtYXMtYS1oaXN0b2dyYW0tb24tb25lLXBhZ2UtdXNpbmctZ2dwbG90CgoKCgoKCgojIEFub3RoZXIgc291cmNlCgoKI3BhcGVyCmh0dHBzOi8vZGwuYWNtLm9yZy9kb2kvcGRmLzEwLjExNDUvMzMzOTgyNS4zMzk0OTI2CgprYWdnbGUgaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hcmFzaG5pYy9maXRiaXQvZGlzY3Vzc2lvbi8zMTM1ODk/cGFnZT0yCgoKdGhpcyBpcyBpdDoKCmh0dHBzOi8vd3d3LmNkYy5nb3YvcGh5c2ljYWxhY3Rpdml0eS9kYXRhL2luYWN0aXZpdHktcHJldmFsZW5jZS1tYXBzL2luZGV4Lmh0bWwjUmFjZS1FdGhuaWNpdHkKCgoKaHR0cHM6Ly93d3cuYmxzLmdvdi90dXMvZGF0YS9kYXRhZmlsZXMtMDMyMS5odG0KCgpodHRwczovL3d3dy5jZGMuZ292L25jaHMvcHJvZHVjdHMvZGF0YWJyaWVmcy9kYjQ0My5odG0KCgpodHRwczovL3d3dy5jZGMuZ292L25jaHMvcHJvZHVjdHMvZGF0YWJyaWVmcy9kYjQ0My5odG0KCiMgUmVmZXJlbmNlOgotIEVEQTogaHR0cHM6Ly9ycHVicy5jb20vam92aWFsL3IgCgotIEhpc3RvZ3JhbXM6IGh0dHBzOi8vc3RhdGlzdGljc2J5amltLmNvbS9iYXNpY3MvaGlzdG9ncmFtcy8KaHR0cHM6Ly9ibG9nLm1pbml0YWIuY29tL2VuLzMtdGhpbmdzLWEtaGlzdG9ncmFtLWNhbi10ZWxsLXlvdQoKQ2F0ZWdvcmljYWwsIG9yZGluYWwsIGludGVydmFsLCBhbmQgcmF0aW8gdmFyaWFibGVzIDogaHR0cHM6Ly93d3cuZ3JhcGhwYWQuY29tL2d1aWRlcy9wcmlzbS9sYXRlc3Qvc3RhdGlzdGljcy90aGVfZGlmZmVyZW50X2tpbmRzX29mX3ZhcmlhYmwuaHRtCgpBZGQgZGVuc2l0eSBsaW5lIHRvIGhpc3RvZ3JhbTogaHR0cHM6Ly9yLWNvZGVyLmNvbS9kZW5zaXR5LXBsb3QtcgoKLSBFcnJvciBiYXJzIHZzIENJOiBodHRwczovL2Jsb2dzLnNhcy5jb20vY29udGVudC9pbWwvMjAxOS8xMC8wOS9zdGF0aXN0aWMtZXJyb3ItYmFycy1tZWFuLmh0bWwK